很棒的计数DP题.

Description

若正整数集合$X \sub [1,n], Y \sub [1,M], X \cap Y = \empty$, 且有$\bigoplus_X < \bigoplus_Y$, 求方案数.

Solution

先考虑一个朴素的DP:

$f(i,j,k)$ 表示决策第$i$个数字, $\bigoplus_X = j, \bigoplus_Y = k$的方案数. 每次转移只要决策是把数字$i$给X, Y或者都不给即可.

最后取所有$j < k$的$f(\max(n,m), j, k)$即可. 这是$O(n^3)$的, 无法通过此题.

我们考虑枚举$X$和$Y$异或和的LCP, 那么对于枚举的长度$i$, 合法的状态一定是: $X \oplus Y$的高$i - 1$位均为0, 且$i$位为1, 且$X$第$i$位为0. 于是我们可以这样设计状态: $f(i,j,0/1)$表示决策第$i$个数, $X \oplus Y$为$j$, $X$的第$b$位为$0/1$, $b$是枚举的. 由于无论把$i$分给谁, 作用在$X \oplus Y$上的效果都是一样的, 所以我们可以削减状态量.

这样DP的复杂度就降到了$O(n^2\log{n})$.

题解还提出了更优的复杂度. 对于枚举到第$b$位的情况, 我们状态中只记录异或和的高$b$位, 这样总的复杂度就降到了$O(n^2)$.

WinterAndSnowmen.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <bits/stdc++.h>

using namespace std;

const int maxn = 2005;
const int maxs = (1 << 11);
const int mod = 1e+9 + 7;

int dp[maxn][maxs][2], n, m, cur;

class WinterAndSnowmen {
public:
int f(int t, int x, int bt) {
if (dp[t][x][bt] != -1) return dp[t][x][bt];
int ret = 0;
if (t == 0) {
if (x == 1 && bt == 0)
ret = 1;
else ret = 0;
} else {
ret = f(t - 1, x, bt);
if (t <= n) {
ret = (ret + f(t - 1, x ^ (t >> cur), bt ^ ((t >> cur) & 1))) % mod;
}
if (t <= m) {
ret = (ret + f(t - 1, x ^ (t >> cur), bt)) % mod;
}
}
return dp[t][x][bt] = ret;
}
int getNumber(int N, int M) {
n = N; m = M;
int ans = 0;
for (cur = 0; cur < 11; ++cur) {
memset(dp, -1, sizeof dp);
ans = (ans + f(max(n, m), 0, 0)) % mod;
}
return ans;
}
};