皆大好き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らしいような気もするし強い人は気づくのかな。天才になりたいものです。