ABC138: F - Coincidence
問題概要
とが与えられる。このとき かつ を満たす整数の組の個数を求めよ。
(問題文は https://atcoder.jp/contests/abc138/tasks/abc138_f に記載されている。)
制約
アイディア
の条件下で が成り立つことが証明できる。よって, が成り立つときには必ず が成り立つ。また,このときをを割ったときの商はとなる。逆に,をを割ったときの商はであるときが成り立つため,条件「」は条件「かつをを割ったときの商が」と言い換えられ,さらにであるため条件「をを割ったときの商が」は と言い換えられ,最終的に「かつ」となる。よって,条件をまとめると以下の5つである。
そして,実はをあとは,上4つに関するフラグを持ちながらを保つように遷移を行いつつ地道にDPを行えばよい。
2019/08/19追記: が成り立つとき も成り立つため, に関するフラグは不必要です。コード例では に関するフラグを持たずに実装しています。
実装
を上から 桁目まで見てかつyの最後の桁がであるときの組み合わせの個数とする。ただし,はそれぞれ
- : が確定しているかどうか
- : が確定しているかどうか
- : が確定しているかどうか
- : が確定しているかどうか
を示すフラグである。
今見ている桁が上から 桁目であるとし, の上から 桁目をそれぞれ とし, に付け加える桁を とすると,
- が false でかつ のとき ではないことが確定するので NG
- が false でかつ のとき ではないことが確定するので NG
- が false でかつ のとき ではないことが確定するので NG
- が false でかつ のとき ではないことが確定するので NG
- かつ のとき ではないことが確定するので NG
これらのケースを除外したあとで
- : が true または ならば true
- : が true または ならば true
- : が true または ならば true
- : が true または ならば true
として, に を足せばよい。
計算量はであるが,定数が比較的重い。
2019/08/19追記: 先程のアイディアで追記したとおり,フラグ は不必要であるため下の実装例では持っていない。また,下の実装例ではフラグ をbitによって管理している。
コード
#include "bits/stdc++.h" using namespace std; typedef long long ll; const ll MOD = (ll)(1e9+7); ll dp[63][8][2]; int main(void){ int i,j,k,l,r,p,s,t; ll L,R,ans=0ll; dp[0][0][0] = 1ll; cin >> L >> R; for(i=0; i<60; ++i){ l = (!!(L&(1ll<<(59-i)))); r = (!!(R&(1ll<<(59-i)))); for(s=0; s<2; ++s){ for(t=0; t<2; ++t){ if(s==1 && t==0){ continue; } for(j=0; j<8; ++j){ for(p=0; p<2; ++p){ if((!(j&1)) && (l > s)){ continue; } if((!(j&2)) && (t > r)){ continue; } if((!(j&4)) && (p > s)){ continue; } k = 0; if((j&1) || (l < s)){ k += 1; } if((j&2) || (t < r)){ k += 2; } if((j&4) || (p < s)){ k += 4; } dp[i+1][k][t] += dp[i][j][p]; dp[i+1][k][t] %= MOD; } } } } } for(j=0; j<8; ++j){ for(p=0; p<2; ++p){ if(!(j&4)){ continue; } ans += dp[60][j][p]; ans %= MOD; } } cout << ans << endl; return 0; }
感想
コンテスト中に通せなくて少しショックだったけど自分で思いつけてよかった...