GDOI2016模拟8.13挑选玩具

题目
ABC找到N个箱子,箱子里装着一些玩具,一共有M种玩具,编号从1到M,同一种玩具可能出现在多个箱子里。

ABC决定从中选择一些箱子,把这些箱子中的玩具聚集到一起,必须保证每种玩具至少出现一次。

问ABC一共有多少种选择方案。

这题,正难则反,可以用总的方案数(除去不选)减去至少缺一个位的,这个可以用容斥算。

对于至少缺一个我们可以枚举缺哪一位,如果知道哪些箱子是那一位缺一个的,求出sum,然后ans-(2 sum -1),由于多减了缺2个的就用同样的方法加回去,一直容斥下去。

那么关键是我们如何求至少缺i个的箱子数,这个我们将其变成最多缺i个,将方案排列到一个数组上,会发现,每次二分一个点,将数组分开两半,那么左半边是右半边的子集,且一一对应,这样边二分,边将左半边方案一一对应加到右半边,可以nlogn预处理出来,做的时候反着做就行了。

贴代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#define N 2000001
#define MOD 1000000007
#define M 21
using namespace std;
int n,m,ans;
int f[N],help[M];
void init(){
    static int x,y,z;
    scanf("%d %d",&n,&m);
    help[0]=1;
    for (int i=1;i<=m;i++)
        help[i]=help[i-1]+help[i-1];
    for (int i=1;i<=n;i++){
        x=0;
        scanf("%d",&y);
        for (;y--;)
            scanf("%d",&z),x|=help[z-1];
        f[x]++;
    }
}
void dfs(int l,int r){
    static int x,y;
    if (l==r)return;
    dfs(l,(l+r)/2),dfs((l+r)/2+1,r);
    x=(l+r)/2;
    y=x-l+1;
    for (int i=0;i<y;i++)
        f[x+i+1]+=f[l+i];
}
void pre(){
    dfs(0,help[m]-1);
}
bool did(int x){
    static bool p;
    p=0;
    while (x){
        p^=1;
        x-=(x&-x);
    }
    return p;
}
int calc(int x,int y){
    static int s;
    if (!y)return 1;
    s=calc(x,y/2);
    s=(long long)s*s%MOD;
    return (y&1)?(long long)s*x%MOD:s;
}
void work(){
    for (int i=0;i<help[m];i++)
        if (!did(i))
            ans=((long long)ans+calc(2,f[i^(help[m]-1)])-1+MOD)%MOD;
        else
            ans=((long long)ans-calc(2,f[i^(help[m]-1)])+1+MOD)%MOD;
}
void write(){
    printf("%d",ans);
}
int main(){
    init();
    pre();
    work();
    write();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值