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,之后剩下的数共有22n−k−1 种选择方案。我们此次举出的是至少有k个交集的方案。
之后运用容斥原理,斥掉k+1,容上k+2……
公式为Ckn∗C0n−k∗22n−k−Ckn∗C1n−k∗22n−k−1+Ckn∗C2n−k22n−k−2 ……
对于22n−k ,用降幂大法,先求出x=2n−kmod(p−1),再求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;
}