bzoj 4762: 最小集合

明天就是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 \bigwedge _{a\in S} (f(S_{a})\neq0)]$$

 

这玩意是很难直接dp的,因此容斥这些限制

也就是说要去求补集:求某些限制同时不合法的方案数

于是有这么一个式子:

$$[\bigwedge _{a\in S} (f(S_{a})\neq0)]= \sum_{S'\subseteq S}[\bigwedge _{a\in S'} (f(S_{a})=0)](-1)^{|S'|}$$

注意到$$[\bigwedge _{a\in S'} (f(S_{a})=0)] = [\text{OR}_{a\in S'}f(S_a) = 0]$$

也就是如果某些限制同时不满足的话,那么如果把所有对应集合的AND和 按位或起来的和为0

 

因此答案就等于$$\sum_{S}\sum_{S'\subseteq S}[(f(S) = 0) \wedge \text{OR}_{a\in S'}f(S_a) = 0](-1)^{|S'|}$$

暴力的话枚举集合\(S\),枚举\(S\)的子集\(S'\)就可以算贡献了

如果要DP的话只需要记录\(S\)的AND和,\(S'\)的\(\text{OR}_{a\in S'}f(S_a)\),对应的贡献的话就可以DP了

 

令\(f_{i,k,j}\) 为 考虑了前\(i\)个数,前者的值为\(k\),后者的值为\(j\)的答案

若第\(i + 1\)个数为\(d\)

如果不选第\(i + 1\)个数,则有:\(f_{i, k, j} \to f_{i + 1, k, j}\)

如果\(S\)中选第\(i + 1\)个数,但\(S'\)中不选,则有:\(f_{i, k, j} \to f_{i + 1, k \& d, j \& d}\)

如果\(S\)中选第\(i + 1\)个数,且\(S'\)中也选,则有:\(-f_{i, k, j} \to 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]);
}

 

posted @ 2017-03-22 16:33  AwD!  阅读(1078)  评论(0编辑  收藏  举报