TopCoder SRM 605 Div1 Medium AlienAndSetDiv1
問題
自然数N, Kが与えられる。
{1, 2, ..., 2*N}の集合を互いに素なN個ずつの集合A, Bにわける。
A, Bの要素を小さい順に並べたとき、どのiについても|A[i] - B[i]| ≧ KとなるようなA, Bの選び方は何通りあるか、mod 10^9 + 7で求めよ。
制約条件
N≦50
K≦10
方針
O(N^2)でできるじゃーんとかいう電波を受信してハマってた。
A, Bに1から順に要素を詰めていくDP.
今のところのAの要素をa個、Bの要素をb個とする。
常にa≧bであるとしてよい。
問題なのはAの新しいほうからa-b個の部分で、この部分をbitで表現できればいい。
集合の選び方への干渉がありうるのは、今追加する数をxとしたら、xとの差が1〜k-1であるもの。
なのでa-b個に入ってるもののうち、差が1〜k-1であるようなものだけbitで持てばいい。
このbitに、1がa-b個立っていたら、Bへの追加はできないということ。
計算量はO(N^2 logK)なんだけど、K-1桁分しか見ないし、a≧bで1/2がかかるし、
実際に遷移する状態もあんまり多くないしで、mapを使っても0.5s弱くらいで余裕で通る。
ソースコード
const int mod = (int)1e9 + 7; int n, k, mask; map<pair<pi, int>, int> dp; int rec(int a, int b, int bit){ if(a == n && b == n) return 1; if(a > n) return 0; if(dp.count(mp(mp(a, b), bit))) return dp[mp(mp(a, b), bit)]; int &res = dp[mp(mp(a, b), bit)]; res = rec(a + 1, b, (bit << 1 | 1) & mask); if(a == b) res *= 2; if(__builtin_popcount(bit) < a - b) res += rec(a, b + 1, (bit << 1) & mask); return res %= mod; } class AlienAndSetDiv1 { public: int getNumber(int N, int K) { n = N; k = K; mask = (1 << k - 1) - 1; //1〜k-1だけ覚える dp.clear(); return rec(0, 0, 0); } };