D - I Wanna Win The Game

[Q] https://atcoder.jp/contests/arc116/tasks/arc116_d

難しかった。
dfsで愚直解TLEしたあと、規則性があることに気づいてメモDP。
O(MlogM)っぽくて29ms。

DP[何bit以下で構成されるか][M] = 何通り?
でデータ管理。
M=5000 < 2^12*2 なので、bitは0~11桁目まで見ればよさそう。
解答は DP[11][M] になる。
DP=0通りも必要情報なので、DP=-1で初期化しておく。

Q. どんな規則性?

入力例
5 50
を考える。

・bitで表示させると
50 = 110010
   = 022002
   = 020242
   = 004242 ... などと表せる。
xor0になるためには、各bit桁を偶数個選ばないといけない。

・最上位bitと、それ未満で切り分けて考える。
1. 4bit目に2をとる場合
 0 0 0 0 0 0 0<2>2 0 0 2 
 0 0 0 0 0 0 0<2>0 2 4 2 
 0 0 0 0 0 0 0<2>0 4 0 2 

DP[4][50] += 5C2 * DP[3][50-16*2] でまとめたい。
                // DP[3][18] = {2002, 0242, 0402} の取り方を表す。

Q. 5C2 ?
A. N=5なので、任意の5項のうち、1<<4=16を 任意の2項に選ぶ組み合わせなので5C2。

Q. DP[3][18] ?
A. DP[ 何bit目以降で構成されるか ][ 総和 ]で管理。
4bit目に2をとるのを確定させたので、残りの 0~3bit で 18 になるような取り方をDPできるとうれしい。

2. 4bit目に0をとる場合
 0 0 0 0 0 0 0<0>4 2 4 2 
 0 0 0 0 0 0 0<0>4 4 0 2 

DP[4][50] +=  5C0 * DP[3][50]

Q. DP[3][50] ?
A. bitを下げた処理に進む。

Q. DP[3][18]?
A. もう1思考整理する。
最上位bitをNCkし、切り分ければよかった。

・DP[3][18]

18 のbit表示は
 0 0 0 0 0 0 0 0 2 0 0 2  ...(1)

 0 0 0 0 0 0 0 0 0 2 4 2  ...(2)
 0 0 0 0 0 0 0 0 0 4 0 2  ...(2)

(1) DP[3][18] += 5C2 * DP[2][2] // 2002 - 2000 = 002(下位2bit, 2) が次のDP
(2) DP[3][18] += 5C0 * DP[2][18] // 次の処理にすすむ

実装。

//nCk
const ll MAX = 5010;

long long fac[MAX], finv[MAX], inv[MAX];

void COMinit(){
 fac[0] = fac[1]=1;
 finv[0] = finv[1]=1;
 inv[1] = 1;
 for(int i=2; i<MAX; i++){
   fac[i] = fac[i-1] * i % MOD;
   inv[i] = MOD-inv[MOD%i] * (MOD/i) % MOD;
   finv[i] = finv[i-1] * inv[i] % MOD;//逆元の階上
 }
}

ll COM(int n, int k){
 if (n<k)
   return 0;
 if (n<0 || k<0)
   return 0;
 return fac[n] * (finv[k] * finv[n-k] % MOD) % MOD;
}




ll N, M;
ll DP[12][5010]; // DP[bits][m]
// 解答はDP[11][M]

ll dfs2(ll bit, ll m){
 if (DP[bit][m]>-1) return DP[bit][m]; // 見たことがあるならおしまい

 if (m==0) return DP[bit][m] = 1; // 0000の1通りだけ
 if (bit==0){ // N項に1を割り振れる? {1,1,1,1,0,0} とか。
   if (m>N) return DP[bit][m] = 0;
   else return DP[bit][m] = COM(N, m);
 }

 ll ret = 0; 
 for(ll k=0; (1<<bit)*k<=m; k+=2){// 0000 -> 2000 -> 4000 ...
   if (k>N) break;
   ( ret += COM(N, k) * dfs2(bit-1, m-(1<<bit)*k) ) %= MOD;
 }
 
 return DP[bit][m] = ret;
}

int main(){
 cincout();
 COMinit(); // nCkの初期入れ
 
 cin >> N >> M;
 if (M%2 || N==1){
   cout << 0 << endl;
   return 0;
 }
 
 rep(i, 12) rep(j, 5001) DP[i][j]=-1;
 cout << dfs2(11, M) << endl; // DP[11bit][M]が解答。
}


https://atcoder.jp/contests/arc116/submissions/26205361


この記事が気に入ったらサポートをしてみませんか?