皆大好きXor Sum。解き直したので。
リンク:D - Xor Sum
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
問題文
正の整数 が与えられます。
2 つの整数 であって、ある非負整数
が存在して、
となるようなものが何通りあるかを求めてください。 ここで、
はビットごとの排他的論理和を表します。 なお、答えは非常に大きくなることがあるので、
で割った余りを求めてください。
制約
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
難しい。 が大きすぎて何もできない。
最終的には になるんだけどその解説も天才すぎる。
https://atcoder.jp/img/arc066/editorial.pdf
桁dpということを強く意識して解いてみる(結局やることは解説まんま)
解説前半部分の だけ考えれば十分って部分は典型なのかな。
基本情報技術者の勉強で半加算器とか見てると分かりそうな気もする。
結局問題としてはを満たす
の組み合わせの数の桁dp(正確にはbit桁dpかな)
まずを2進に直す(bitsetとかvector < bool > とか)
なので
、60桁あれば十分。
桁dpなので上の桁から決めていく。解説通りにdpをとる。
「と
の
ビット目以上が決定していて、その段階で
となる通り数。」
最初何言ってるか分からなかった。天才過ぎて辛い。
具体的にやることはのビットを上から見て、それ以下になるような数を数え上げる。
(普通の桁dpと同じで、上位の桁がある程度決まると下位の桁をどうとっても以下になるという性質を利用する)
dpテーブルはととり(0-indexed)、初期化は
それ以外は
。
(このというのは
の60桁目のビット(0-indexed,また制約より必ず0)に対して
の60桁目のビット(0-indexed)をともに
ととることで必ず題意を満たすことができるので)(簡単に言うと
)(それはそう)
まず先にコードを書くとこんな感じ。
#include <iostream> #include <vector> #include <bitset> using namespace std; using ll = long long; const ll MOD = (ll)1e9 + 7; int main() { ll N; cin >> N; bitset<60> bs(N); //2進に直す vector<vector<long long>> dp(61, vector<long long>(3, 0));//dpテーブル dp[60][0] = 1;//初期化 //上の桁から見ていく for (int i = 59; i >= 0; --i) { //i桁目のbitが立ってるとき if (bs[i]) { dp[i][0] = dp[i + 1][0]; dp[i][1] = dp[i + 1][0] + dp[i + 1][1]; dp[i][2] = 2 * dp[i + 1][1] + 3 * dp[i + 1][2]; } //i桁目のbitが立ってないとき else { dp[i][0] = dp[i + 1][0] + dp[i + 1][1]; dp[i][1] = dp[i + 1][1]; dp[i][2] = dp[i + 1][1] + 3 * dp[i + 1][2]; } dp[i][0] %= MOD; dp[i][1] %= MOD; dp[i][2] %= MOD; } cout << (((dp[0][0] + dp[0][1]) % MOD) + dp[0][2]) % MOD << endl; return 0; }
のビットを上から見ていく(for int i = 59; 0 <= i; --i)
イメージとしてはこんな感じ。

(今このは適当な
)
このときが何を表しているかというと
「 と
の
ビット目以上が決定していて、その段階で
となる通り数。」である。ここが一番分かりにくい気がする。
の
桁目から
桁目までの2進数を一つの数
、
の
桁目から
桁目までの2進数を一つの数
、
の
桁目から
桁目までの2進数を一つの数
として見たとき
は
となる通り数。
は
となる通り数。
は
となる通り数。である(これでも分かりにくいけど)

ここで次のビットである桁目を考えると
の
桁目が
のとき
、
の
桁目が
のとき
となる(2進数の定義より)
場合分けして丁寧に遷移を考える。
(1)の
桁目が
のとき
より
について以下のようになる。
は
となる通り数。
は
となる通り数。
は
となる通り数。
ここで
は
となる通り数。
は
となる通り数。
は
となる通り数、だったので
の遷移を考えると2倍して0 or 1 or 2を足すという操作ができる。
(の
桁目のビットについて、共に1、どちらか一方が1、共に0の高々3種類なので)
よって以下のような遷移を考えればよい。

従って
は
に対して
の1通りのみで
は
に対して
の1通り、
に対して
の1通り、計2通りで
は
に対して
の2通り、
に 対して
の3通り、計5通りで
(2)の
桁目が
のとき
より
について以下のようになる。
は
となる通り数。
は
となる通り数。
は
となる通り数。
(1)と同様に以下のような遷移を考えればよい。

従って
は
に対して
の1通り、
に対して
の1通り、計2通りで
は
に対して
の1通りのみで
は
に対して
の1通り、
に 対して
の3通り、計4通りで
以上をまとめて
(1)の
桁目が
のとき
(2)の
桁目が
のとき
以上の遷移を行うことで が答えとなる。
難しすぎる。700点でしょ(800点でもいい)
ビットの遷移がしかないので
の状態をまとめられるってのが凄い、ある意味桁dpらしいような気もするし強い人は気づくのかな。天才になりたいものです。