小P的团战

20191005 T2

原题链接

By GKurumi

题目描述:

升级过技能的小P已经具备开团能力,他现在有N个技能,准备单挑对面的M个人

但小P血非常少,所以他必须要控制住对面所有M个人,不然就会死掉

已知第i种技能可以控制住Ki个人,分别为\(a_{i1},a_{i2}……,a_{ik}\)

问有多少种放技能的方式控住对面所有\(M\)个人(控制技能只能放一次,只有顺序不同的方式被认为是同一种)

答案对1000000007取模

输入格式:

第一行两个整数\(N\)\(M\)

接下来N行,每行第一个整数表示Ki,接Ki个整数表示\(a_{i1},a_{i2}……,a_{ik}\)

输出格式:

一个整数表示放技能的方式数目

样例

样例输入
4 5
2 2 3
2 1 2
4 1 2 3 5
4 1 2 4 5

样例输出

6

题解:

​ 首先,我是我们机房最菜的人

​ 这道题乍一看有点像组合计数,但是我不会 太麻烦了推不出来,于是我就回想起那个月黑风高的夜晚发生的事统计方案用状压,于是就走上了一条不归路。

​ 思路:我们用一个数组存下每个技能能打到的位置,之后开始状压dp,dp的时候在用另一个数组存此时用技能打第i位上敌人的方案数。跑一遍下来之后,再遍历查找一遍位置,当i位上有方案的时候就加上方案数,无方案的时候就减去(因为我一开始算了一遍总的方案数(鬼知道我怎么想的)(可能太想跟计数原理扯上关系)我爱计数原理!!也可以直接计算方案数,看个人的喜欢。
转移方程:if(i&(1<<j))f[i^(1<<j)]+=f[i],w[i]++;
计算方案数:(ans+=(w[i]&1?-1:1)*ksm(2,f[i]))%=mod;

以防出锅最后ans如果成了负数,emmm,就这么mod:(ans+mod)%mod

考试时卑微的代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define mod 1000000007
int f[1<<20],w[1<<20];
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch-'0');ch=getchar();}
    return x*f;
}
int ksm(int a,int b)
{
    int ans=1;
    while(b)
    {
        if(b&1)ans=(ans*a)%mod;
        b>>=1;
        a=(a*a)%mod;
    }
    return ans;
}
int n,m,x,k,sum,ans;
signed main()
{
    freopen("carry.in","r",stdin);
    freopen("carry.out","w",stdout);
    n=read();m=read();
    sum=(1<<m)-1;
    for(int i=1;i<=n;i++)
    {
        k=read();
        int w=0;
        for(int j=1;j<=k;j++)
        {
            x=read();
            w|=(1<<(x-1));
        }
        f[w^sum]++;
    }
    ans=ksm(2,n);
    for(int j=0;j<m;j++)
    {
        for(int i=(1<<m)-1;i!=0;i--)
        {
            if(i&(1<<j))f[i^(1<<j)]+=f[i],w[i]++;
        }
    }
    for(int i=1;i<(1<<m);i++)
    {
        (ans+=(w[i]&1?-1:1)*ksm(2,f[i]))%=mod;
    }
    cout<<(ans+mod)%mod;
    return 0;
}

本人第一次写博客,请多见谅。

如有转载,请标明出处,谢谢。

猜你喜欢

转载自www.cnblogs.com/GKurumi/p/11624879.html