TopCoder SRM 555 Div1 Medium XorBoard
問題
HxWのグリッドがあり、はじめ全てのグリッドは0である。
1行を自由に選んでその0, 1を反転する操作を合計Rcount回行い、
1列を自由に選んでその0, 1を反転する操作を合計Ccount回行う。
操作を行った後で、1の個数がS個になっているような、
操作の選び方は何通りあるか、mod 555555555で求めよ。
ただし、どの行、列が何回操作が行われたかのみを区別し、操作の順序は区別しないものとする。
制約条件
H, W≦1555
Rcount, Ccount≦1555
S≦H * W
方針
同じ行、列を2度選ぶと、操作は打ち消しあう。
なので、打ち消されていない操作を、a行、b列に対して行ったとき、
1の数はHb + Wa - ab個になる。
a, bは1500以下くらいなので、全部ループをまわしてよい。
Hの行の中からa個の行を選ぶ操作(残りは打ち消しあう)が何通りあるかが、速く計算できればよい。
Hの中からa行を選ぶ場合の数がC(H, a)通り。
Rcount - a個の操作は打ち消し合う。
打ち消し合う操作の選び方は、(Rcount - a) / 2個の区別しないものを、H個の箱に入れる場合の数
に等しい。
これは重複順列なので、C(物+箱-1, 物)通り。
ソースコード
多分本番中のやつか何かなので汚い。
const ll mod = 555555555; ll null[1600][800]; //row, use ll C[1600][1600]; class XorBoard { public: int count(int H, int W, int Rcount, int Ccount, int S) { rep(i, 1600) rep(j, 1600) C[i][j] = j == 0 || i == j ? 1 : (C[i - 1][j] + C[i - 1][j - 1]) % mod; memset(null, 0, sizeof(null)); null[0][0] = 1; for(int i = 1; i < 1600; i++) rep(j, 800){ null[i][j] = (null[i - 1][j] + (j ? null[i][j - 1] : 0)) % mod; } ll ans = 0; rep(tate, W + 1){ if(W - 2 * tate == 0){ if(S != H * tate) continue; rep(yoko, H + 1){ if(Ccount < tate || Rcount < yoko) continue; if((Ccount - tate) % 2 || (Rcount - yoko) % 2) continue; ans += C[W][tate] * null[W][(Ccount - tate) / 2] % mod * (C[H][yoko] * null[H][(Rcount - yoko) / 2] % mod) % mod; ans %= mod; } continue; } if((S - tate * H) % (W - 2 * tate)) continue; int yoko = (S - tate * H) / (W - 2 * tate); if(yoko < 0 || yoko > H) continue; if(Ccount < tate || Rcount < yoko) continue; if((Ccount - tate) % 2 || (Rcount - yoko) % 2) continue; ans += C[W][tate] * null[W][(Ccount - tate) / 2] % mod * (C[H][yoko] * null[H][(Rcount - yoko) / 2] % mod) % mod; ans %= mod; } return ans; } };