P6442 [COCI2011-2012#6] KOŠARE

题目传送门1
题目传送门2


思路

如果我们设 \(f[S]\) 为含有 \(S\) 中的玩具的方案数,我们只能用普通的状压DP,但复杂度高达 \(O(n2^m)\),也就是 \(O(n^2)\) 级别

正难则反,我们考虑设 \(g[S]\) 表示含有 \(S\) 中的玩具的方案数,那么答案应该是 \(\sum (-1)^{||S||}g[S]\)\(||S||\) 表示 \(S\) 在二进制下的位数)

考虑如何求 \(g[S]\)

我们设 \(f[S]\) 表示不含有 \(S\) 中玩具的箱子的个数,那么 \(g[S]=2^{f[S]}-1\)

\(f[S]\) 可以用高维前缀和求出


代码

#include<iostream>
#include<fstream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#define LL long long
#define FOR(i, x, y) for(int i = (x); i <= (y); i++)
#define ROF(i, x, y) for(int i = (x); i >= (y); i--)
#define PFOR(i, x) for(int i = he[x]; i; i = r[i].nxt)
inline int reads()
{
    int sign = 1, re = 0; char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') sign = -1; c = getchar();}
    while('0' <= c && c <= '9'){re = re * 10 + (c - '0'); c = getchar();}
    return sign * re;
}
namespace MOD
{
    const int mod = 1e9 + 7;
    inline int add(int a, int b) {return a + b >= mod ? a + b - mod : a + b;}
    inline int mul(int a, int b) {return 1ll * a * b % mod;}
    inline int sub(int a, int b) {return a - b < 0 ? a - b + mod : a - b;}
    inline int fast_pow(int a, LL b)
    {
        int re = 1;
        while(b)
        {
            if(b & 1) re = mul(re, a);
            a = mul(a, a);
            b >>= 1;
        }
        return re;
    }
} using namespace MOD;
int n, m, all, ans;
LL f[1 << 20];
inline int get_pop(int x)
{
    int re = 0;
    while(x) re += x & 1, x >>= 1;
    return re;
}
signed main()
{
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    n = reads(), m = reads(), all = (1 << m) - 1;
    FOR(i, 1, n)
    {
        int k = reads(), S = 0;
        FOR(j, 1, k)
        {
            int a = reads();
            S |= (1 << (a - 1));
        }
        f[all ^ S]++;
    }
    FOR(i, 0, m - 1) ROF(j, (1 << m) - 1, 0)
        if(!(j & (1 << i))) f[j] += f[j ^ (1 << i)];
    FOR(i, 0, (1 << m) - 1)
    {
        int k = get_pop(i);
        if(k & 1) ans = sub(ans, sub(fast_pow(2, f[i]), 1));
        else ans = add(ans, sub(fast_pow(2, f[i]), 1));
    }
    printf("%d", ans);
    return 0;
}
posted @ 2022-08-13 09:00  zuytong  阅读(53)  评论(0编辑  收藏  举报