题意(From Luogu):

题目描述

现有 $N$ 个玩具箱,每个玩具箱里都有一些玩具,这些玩具编号为 $1 \dots M$ ,一种玩具可以在多个玩具箱中出现,但每个玩具箱中每种玩具最多只有一个。

现在求:从这些玩具箱中选出一部分使得每种玩具都有的方法数。由于答案很大很大所以 $\bmod 1\ 000\ 000\ 007$ 。

输入格式

第一行两个整数 $N,M$ 。

接下来 $N$ 行每行描述一个玩具箱,第一个整数 $K_i$ 表示这个玩具箱里有多少玩具,接下来 $K$ 个整数表示这个箱子里的玩具。

输出格式

一行一个整数,总的方法数 $\bmod 1\ 000\ 000\ 007$。


首先状压,用高维前缀和求出 $f[s]$表示 “玩具集合为 $s$的子集的箱子有多少个”

然后设 $g[s]$表示 “玩具集合为 $s$的子集的选择方式有多少种”,则 $g[s] = 2 ^ {f[s]}$

最后 $ans = \sum {(-1) ^ {|U – i|} g[i]}$

其中 $U$表示全集

代码:

#include <bits/stdc++.h>

#define NS (1000005)
#define MS (20)
#define MOD (1000000007)
#define lowbit(a) ((a) & (-(a)))
#define pls(a, b) ((a) + (b) < MOD ? (a) + (b) : (a) + (b) - MOD)
#define mns(a, b) ((a) - (b) >= 0 ? (a) - (b) : (a) - (b) + MOD)

using namespace std;

template<typename _Tp> inline void IN(_Tp& dig)
{
    char c; bool flag = 0; dig = 0;
    while (c = getchar(), !isdigit(c)) if (c == '-') flag = 1;
    while (isdigit(c)) dig = dig * 10 + c - '0', c = getchar();
    if (flag) dig = -dig;
}

int n, m, cnt[1 << MS], Bin[NS], Bc[1 << MS], ans;

int main(int argc, char const* argv[])
{
    IN(n), IN(m), Bin[0] = 1;
    for (int i = 1; i <= n; i += 1)
        Bin[i] = pls(Bin[i - 1], Bin[i - 1]);
    for (int i = 1, a, s; i <= n; i += 1)
    {
        IN(a), s = 0;
        for (int j = 1, b; j <= a; j += 1) IN(b), s |= (1 << (b - 1));
        cnt[s]++;
    }
    for (int i = 0; i < m; i += 1)
        for (int j = 1; j < (1 << m); j += 1)
            if (j & (1 << i)) cnt[j] += cnt[j ^ (1 << i)];
    for (int i = 0; i < (1 << m); i += 1)
    {
        Bc[i] = Bc[i >> 1] + (i & 1);
        if ((m - Bc[i]) & 1) ans = mns(ans, mns(Bin[cnt[i]], 1));
        else ans = pls(ans, mns(Bin[cnt[i]], 1));
    }
    printf("%d\n", ans);
    return 0;
}
分类: 文章

XZYQvQ

炒鸡辣鸡的制杖蒟蒻一枚QvQ

0 条评论

发表回复

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用 * 标注