Description
定义一个非负数
是
长度下的
的半倍数,为
不是
的倍数,且
是
的倍数。
问有多少个
长度下的
的半倍数。
Difficulty
MainAlgorithm
容斥
DP
Complexity
Solution
比较难……
我们肯定是要对每个余数分别讨论的,可以用
的DP很轻松地把
以下的所有模
为
的数搞出来。
这样就能发现只有余数
或
时才有可能是半倍数的。
但是也有不合法的,当且仅当应该减去
的位都是0,应该加上
的位都是1。
对于每个模数都去DP这个不合法的是
的,显然不行。我们要继续挖掘性质。
我们发现,当
时,所有数都不合法。
当
是个偶数时,我们讨论第一位的取值。当第一位取
,只要剩下的
位是
的倍数即可。当第一位取
,只要剩下的
位是
的半倍数即可。
那么我们只需要解决
是个奇数的情况了。而且我们发现
在模奇数意义下在一个小环内!即实际上
存在循环节。
我们先将
的情况DP出来,我们可以直接利用这个DP的结果得到
的结果。需要做的只有将整个DP的答案左移即可。
实际上,当
时,只需要取出在
位处的DP值,并补上前
位的部分即可。
然后一个非常混乱的
的DP就能解决这个问题了。
我的做法稍有不同。
最后对于奇数部分,我们先DP出所有方案,为了减去不合法的,我们应当将DP反向倒退,由于此DP的特殊性,这一步相当于解一个方程,这个方程十分好解,于是就完成了。。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define Rep(i, x, y) for (int i = x; i <= y; i ++)
#define Dwn(i, x, y) for (int i = x; i >= y; i --)
#define RepE(i, x) for(int i = pos[x]; i; i = g[i].nex)
using namespace std;
typedef long long LL;
const int N = 2005, mod = 1000000007, p2 = (mod + 1) >> 1;
LL p0, ans, f[N][N], h[N], g[N];
int d[N], n, m; bool vis[N];
class SemiMultiple {
public:
void Find(int x, int y) {
while (x --) {
memset (vis, 0, sizeof(vis));
Rep(i, 0, m - 1) if (!vis[i]) {
LL sum = 0;
for (int j = i; !vis[j]; j = (j + y) % m) {
(sum += h[j]) %= mod, vis[j] = 1;
} sum = sum * p2 % mod;
bool fl = 0; int j = i;
do {
if (fl) sum = (sum - h[j] + mod) % mod;
fl ^= 1; j = (j + y) % m;
} while (j != i);
// h[m+i-y] = sum;
j = i; do {
h[j] = (h[j] - sum + mod) % mod; sum = h[j]; j = (j + y) % m;
} while (j != i);
}
}
}
int count(int n1, int m1) {
n = n1, m = m1;
while (n && m % 2 == 0) m /= 2, p0 ++, n --;
int r = 1; f[0][0] = 1;
Rep(i, 1, n) {
d[r] ++;
Rep(j, 0, m - 1) f[i][j] = f[i - 1][j];
Rep(j, 0, m - 1) (f[i][(j + r) % m] += f[i - 1][j]) %= mod;
r = r * 2 % m;
} // f[i][j] choose any of them mod m == j
ans = p0 * f[n][0] % mod;
Rep(i, 1, m - 1) if (d[i] || d[m - i]) {
// int x = d[i];
(ans += f[n][i]) %= mod;
Rep(j, 0, m - 1) h[j] = f[n][j];
Find(d[i], i), Find(d[m - i], m - i);
Rep(t0, 1, d[m - i]) {
Rep(j, 0, m - 1) g[(j + m - i) % m] = h[j];
Rep(j, 0, m - 1) h[j] = g[j];
}
ans = (ans - h[i] + mod) % mod;
}
return ans;
}
};