競プロの問題で質問です。
長さNのK以下の数列で、XORを取ったものが0となる数列は何通りあるでしょうか。
たとえば、N=3, K=2のとき、(0, 0, 0), (0, 1, 1), (0, 2, 2), (1, 0, 1), (1, 1, 0), (2, 0, 2), (2, 2, 0) の7通りが答えです。
という問題を解きたいのですが、解くことができません。どのようにすれば解けるでしょうか?制約は N ≦ 1000, K ≦ 2^50 です。
追記
考えたことを追記します。
①__長さNのK以下の数列のXORが0の通り数__ (求めたいもの)
②__長さN-1のK以下の数列のXORがK以下の通り数__
③__長さN-1のK以下の数列のXORがKを超える通り数__
①と②は等しいです。
なぜなら、②のXORの結果と同じ数値を後ろに付け足せば、①の数列になるからです。
そして、③は②の余事象であるため、③を求めることを考えました。
以下のようなDPを考えます。
dp[i][j] := 上からiビット目まで見たときの通り数(jはKを超えたかどうか)
そして次のようなコードを書きました。
(オーバーフローは気にしない方向でお願いします)
cpp
1#include <bits/stdc++.h> 2using namespace std; 3 4int dp[52][2]; 5 6int comb(int x,int y){ 7 if(x<0||y<0||x<y)return 0; 8 int r=1; 9 for(int i=1;i<=y;++i){ 10 r*=(x-i+1); 11 r/=i; 12 } 13 return r; 14} 15 16int main(){ 17 int N; 18 long long K; 19 cin>>N>>K; 20 21 int k=0; 22 long long kk=K; 23 while(kk)k++,kk>>=1; 24 25 int odd=0,even=0; 26 for(int i=1;i<=N-1;i+=2){ 27 odd+=comb(N-1,i); 28 } 29 for(int i=0;i<=N-1;i+=2){ 30 even+=comb(N-1,i); 31 } 32 33 dp[0][0]=1; 34 for(int i=0;i<k;++i){ 35 for(int j=0;j<=1;++j){ 36 if(j==1){ 37 dp[i+1][j]+=dp[i][j]*(even+odd); 38 }else{ 39 if((K&(1LL<<(k-i-1)))==0){ 40 dp[i+1][1]+=dp[i][j]*odd; 41 dp[i+1][0]+=dp[i][j]*even; 42 }else{ 43 dp[i+1][j]+=dp[i][j]*odd; 44 } 45 } 46 } 47 } 48 cout<<pow(K+1,N-1)-dp[k][1]<<endl; 49 return 0; 50}
しかしこのコードでは、数列の最後の要素がKを超えた場合が考慮されていません。
たとえばN=3、K=2のとき、(1, 2, 3), (2, 1, 3)まで数えてしまうため、出力は5です。
どのように書き換えれば 数列の要素がKを超えた場合 を考慮できるかわからず、解決の糸口が見つからなかったため質問いたしました。
追記2
できればヒントだけでも教えていただけると嬉しいです。
ある程度の行間であれば頑張って対応いたします。
回答よろしくお願いします。
回答3件
あなたの回答
tips
プレビュー