明天就是ZJOI Day1啦
恩,实际上很早就写了这一题
然后回来看的时候发现我怎么不会做了……
看着代码尝试理解一下我当时在想什么(捂脸
感觉智商下降的好快QAQ
6.19魔改:
好了,我现在终于知道当时我在写些什么了……
一个AND和为(0)的集合(S)合法,当且仅当它所有大小为(|S - 1|)的集合AND和都不为(0)。
也就是说有(|S|)个限制,每个限制为某个集合的AND和不为(0)
如果令(S_{a})为(S)删去(a)这个元素所形成的集合,令(f(S))为(S)集合中所有元素的AND和
那么答案就是$$sum_{S}[(f(S) = 0) wedge igwedge _{ain S} (f(S_{a}) eq0)]$$
这玩意是很难直接dp的,因此容斥这些限制
也就是说要去求补集:求某些限制同时不合法的方案数
于是有这么一个式子:
$$[igwedge _{ain S} (f(S_{a}) eq0)]= sum_{S'subseteq S}[igwedge _{ain S'} (f(S_{a})=0)](-1)^{|S'|}$$
注意到$$[igwedge _{ain S'} (f(S_{a})=0)] = [ ext{OR}_{ain S'}f(S_a) = 0]$$
也就是如果某些限制同时不满足的话,那么如果把所有对应集合的AND和 按位或起来的和为0
因此答案就等于$$sum_{S}sum_{S'subseteq S}[(f(S) = 0) wedge ext{OR}_{ain S'}f(S_a) = 0](-1)^{|S'|}$$
暴力的话枚举集合(S),枚举(S)的子集(S')就可以算贡献了
如果要DP的话只需要记录(S)的AND和,(S')的( ext{OR}_{ain S'}f(S_a)),对应的贡献的话就可以DP了
令(f_{i,k,j}) 为 考虑了前(i)个数,前者的值为(k),后者的值为(j)的答案
若第(i + 1)个数为(d)
如果不选第(i + 1)个数,则有:(f_{i, k, j} o f_{i + 1, k, j})
如果(S)中选第(i + 1)个数,但(S')中不选,则有:(f_{i, k, j} o f_{i + 1, k & d, j & d})
如果(S)中选第(i + 1)个数,且(S')中也选,则有:(-f_{i, k, j} o f_{i + 1, k & d, k | j & d})
初始状态为(f_{0, 1023, 1023} = 1)
最终答案(ans = f_{n, 0, 0})
于是就好了
#include <bits/stdc++.h> #define N 1030 #define MOD 1000000007 using namespace std; int n; int f[2][N][N], tmp; void ADD(int &t, int d) { t += d; if (t >= MOD) t -= MOD; } int main() { scanf("%d", &n); f[0][1023][1023] = 1; for (int i = 1; i <= n; ++ i) { int d; scanf("%d", &d); tmp ^= 1; for (int j = 0; j < 1024; ++ j) { for (int k = j; k; k = (k - 1) & j) f[tmp][k][j] = 0; f[tmp][0][j] = 0; } for (int j = 0; j < 1024; ++ j) { for (int k = j; k; k = (k - 1) & j) if (f[!tmp][k][j]) { ADD(f[tmp][k][j], f[!tmp][k][j]); ADD(f[tmp][k & d][j & d], f[!tmp][k][j]); ADD(f[tmp][k & d][k | j & d], MOD - f[!tmp][k][j]); } ADD(f[tmp][0][j], f[!tmp][0][j]); ADD(f[tmp][0][j & d], f[!tmp][0][j]); ADD(f[tmp][0][j & d], MOD - f[!tmp][0][j]); } } printf("%d", f[tmp][0][0]); }