bzoj 2839 集合计数

转载请注明出处:

http://www.cnblogs.com/hzoi-wangxh/p/7738616.html

2839: 集合计数

Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 500 Solved: 274
[Submit][Status][Discuss]
Description

一个有N个元素的集合有2^N个不同子集(包含空集),现在要在这2^N个集合中取出若干集合(至少一个),使得
它们的交集的元素个数为K,求取法的方案数,答案模1000000007。(是质数喔~)
Input

一行两个整数N,K
Output

一行为答案。
Sample Input

3 2
Sample Output

6
HINT

【样例说明】

假设原集合为{A,B,C}

则满足条件的方案为:{AB,ABC},{AC,ABC},{BC,ABC},{AB},{AC},{BC}

N<=1000000,k<=N;

题解:
容斥原理。
首先我们选出k个必须选的数Ckn,之后剩下的数共有22nk1 种选择方案。我们此次举出的是至少有k个交集的方案。
之后运用容斥原理,斥掉k+1,容上k+2……
公式为CknC0nk22nkCknC1nk22nk1+CknC2nk22nk2 ……
对于22nk ,用降幂大法,先求出x=2nkmod(p1),再求2xmodp
都乘Ckn,可以提出来最后乘。
注意时刻取模。

附上代码:

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define int long long
int jie[2001000],ny[2001000],mod=1000000007;
int n,k,ans;
int ksm(int,int,int);
int getc(int,int);
signed main()
{
//  freopen("in.txt","r",stdin);
    scanf("%lld%lld",&n,&k);
    int f=1;
    ny[0]=jie[0]=1;
    for(int i=1;i<=n;i++)
        jie[i]=jie[i-1]*i%mod;
    ny[n]=ksm(jie[n],mod-2,mod);
    for(int i=n-1;i>=1;i--)
        ny[i]=ny[i+1]*(i+1)%mod;
    for(int i=k;i<=n;i++)
    {
        int l=getc(n-k,i-k);
        int x1=ksm(2,n-i,mod-1);
        int x2=ksm(2,x1+mod-1,mod);
        ans=(ans+f*l*(x2-1)%mod+mod)%mod;
        f=-f;
    }
    ans*=getc(n,k);
    printf("%lld",ans%mod);
    return 0;
}
int ksm(int x,int y,int p)
{
    int z=x,sum=1;
    while(y)
    {
        if((y&1)==1)
            sum=sum*z%p;
        y=y>>1;
        z=z*z%p;
    }
    return sum;
}
int getc(int x,int y)
{
    return jie[x]*ny[y]%mod*ny[x-y]%mod;
}
posted @ 2017-10-18 21:14  hzoi_wangxh  阅读(198)  评论(0编辑  收藏  举报