BZOJ 2839: 集合计数
题概:
题目描述
输入格式
输出格式
样例
数据范围与提示
关键字:
容斥定理,线性推逆元
思路:
ans=C(n,k)*{sigma(0,i)&&(i&1==0){C(n-k,i)*(pow(2,bin[n-i-k])-1)}
-sigma(0,i)&&(i&1==1){C(n-k,i)*(pow(2,bin[n-i-k]-1)}}
首先,能想到交集的k个数并不能确定,所以最终*C(n,k).k个数是所有符合方案中必备的数,那末只考虑在n-k个数中选出所有符合取不到交集的数量.很自然的可以得到,SUM=2^(2^(n-k))-1(抛去一个都不选的情况),接下来就是kx(开心)地容斥
考虑所谓的SUM是至少有0个交集的方案数,以此类推,我们可以得到sum[i]为至少有i个交集的方案数
最终为sum[0]-sum[1]+sum[2]-sum[3]+...
此时思考为什莫,直接来看是至少为0的-至少为1的.但其实所谓的sum[i]为交集至少为i个的方案数这一定义并不准确
举个栗子:
当求sum[1]时,假设两种情况(必选A)和(必选 B).那莫交集至少为A,或至少为B的方案确实是pow(2,bin[n-i-k])-1
但是在统计sum[1]时乘C(n,1),会把交集至少为AB的方案数重复加,so用sum[2]减回去,以此类推...
emmm
然后就感性地用了容斥
然而证明容斥就先撂了吧
注意:
欧拉定理是用来阐述素数模下,指数同余的性质
优化指数部分,如果要取模,要mod(1000000007-1)
举例:
如计算7222的个位数,实际是求7222被10除的余数
7和10互素,且φ(10)=4
由欧拉定理知74 Ξ 1 (MOD 10)
所以7222 == (74)55 * (72) Ξ 155 * 72 Ξ 49 Ξ 9 (mod 10)
所谓降幂模法
最后
1 #include<iostream> 2 #include<cstdio> 3 #define ll long long 4 #define MAXN 1000010 5 using namespace std; 6 #define mod 1000000007 7 int n,k;//1000000 8 ll inv[MAXN]; 9 ll inv_jc[MAXN]; 10 ll jc[MAXN]; 11 ll pow(ll a,ll b){ 12 ll ans=1; 13 while(b){ 14 if(b&1)ans=ans*a%mod; 15 b>>=1; 16 a=a*a%mod; 17 } 18 return ans%mod; 19 } 20 void init_inv(){ 21 inv[1]=1; 22 inv_jc[1]=1; 23 jc[1]=1; 24 for(int i=2;i<=n;++i){ 25 jc[i]=jc[i-1]*i%mod; 26 inv[i]=(mod-mod/i)*inv[mod%i]%mod; 27 inv_jc[i]=inv_jc[i-1]*inv[i]%mod; 28 } 29 } 30 ll C(int n,int m){ 31 if(!m)return 1; 32 if(n==m)return 1; 33 return jc[n]*inv_jc[n-m]%mod*inv_jc[m]%mod; 34 } 35 ll res; 36 ll bin[MAXN]; 37 void init_bin(){ 38 bin[0]=1; 39 for(int i=1;i<=n;++i)bin[i]=(bin[i-1]<<1)%(mod-1); 40 } 41 int main(){ 42 scanf("%d%d",&n,&k); 43 init_inv(); 44 init_bin(); 45 // cout<<pow(2,1)<<endl; 46 for(int i=0;i<=n-k;++i){ 47 switch(i&1){ 48 case 1:{ 49 res=(res+mod-C(n-k,i)*(pow(2,bin[n-k-i])-1)%mod)%mod; 50 break; 51 } 52 case 0:{ 53 res=(res+C(n-k,i)*(pow(2,bin[n-i-k])-1)%mod)%mod; 54 break; 55 } 56 } 57 // printf("%lld\n",res); 58 } 59 res=res*C(n,k)%mod; 60 printf("%lld\n",res%mod); 61 }