集合计数 (容斥原理)
心路:
{
想了个思路打出来硬干掉了样例,然后发现是错的....
当时直接崩了...烦躁滴很
...其实这个思路和题解大方向上是一样的,想到了用至少含k个的方案数减去含k+1个的加上k+2的。。。
然后再想怎么求至少含k个的方案数想到了让集合含这k个数然后随机组就行,但没有想出来怎么求含这k个数的集合数,而且就算求出来发现不能直接对一种情况乘,有重复计算的...
想到这...再想...再想...放弃->颓题解。T_T
颓完题解,发现我是把题目信息忽略了...2^n没怎么用上;
}
题解
{
往容斥上想,容斥的一般思路就是先找出单一满足的,把问题简单化(让其限制变小),再逐步用容斥求解。
这题总思路就是先找至少含k个的(限制变少,能够推式),再容斥。
再求至少含k个的时候,容易发现想要交出含这k个的,让集合含这k个数在随机组合一定能交出来.
问题来了:怎么求含特定k个数的集合总数呢?
把这k个数提取出来剩下的数随机组,能够组成的集合在把k个数补回去不就是答案吗,所以有2^(n-k)种(含空集,实际指的是正好只含这K个数的集合),在让这几个集合随机组一定是满足能交成至少含k个数的方案。注意空集不是,所以是2^( 2^(n-k) )-1种。
举个栗子:数据是4 2
以1,3为例,把1 3提取,剩下的数所组是{2},{4},{2,4},{空},其实就是{1,2,3},{1,3,4},{1,2,3,4},{1,3};这四个集合在随机组成的方案中,空集相当于哪个集合都没取交集为空所以不符合。
求出1,3后乘上C(n,2)不就是交出来至少含k个的方案数了吗?显然不是,,,有重复的啊
比如1,3会求到{1,2,3,4}交{1,3,4},而1,4..3,4也会(当时我就这崩了...)
看重复的有多少啊->对于求k个时交出来是k+1个的会算C(k+1,k)遍以此类推..所以在容斥时只要把重复的倍数减去就行。
设f(k)=C(n,k)*( 2^( 2^(n-k) )-1 ),
答案就是f(k)*C(K,K)-C(K+1,K)*f(k+1)+C(k+2,k)*f(k+2)...;
预处理阶乘和逆元,2^...这里也要预处理不然会WA(我也不知道为哈用快速幂求出的大数据就是不对)。
2^(2^(n-k))=( 2^(2^(n-k-1)) )^2,利用这个性质倒推预处理出来就行了
f
}
#include<cstdio> #include<iostream> using namespace std; #define ll long long const int mod=1e9+7; const int maxn=1000010; int n,k; ll ans,f[maxn]; ll fac[maxn],inv[maxn],qtwo[maxn]; ll qpow(ll a,int b) { ll ans=1; while(b) { if(b&1) ans=ans*a%mod; b>>=1; a=a*a%mod; } return ans; } void init() { fac[0]=1; for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod; inv[n]=qpow(fac[n],mod-2); for(int i=n-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod; qtwo[n]=2; for(int i=n-1;i>=0;i--) qtwo[i]=qtwo[i+1]*qtwo[i+1]%mod; } void F(int x) { ll turn,kp; turn=qtwo[x]-1; f[x]=fac[n]*inv[x]%mod*inv[n-x]%mod*turn%mod; } void rongchi() { for(int i=k;i<=n;i+=2) ans=(ans+ fac[i]*inv[k]%mod*inv[i-k]%mod*f[i]%mod )%mod; for(int i=k+1;i<=n;i+=2) ans=(ans+mod- fac[i]*inv[k]%mod*inv[i-k]%mod*f[i]%mod )%mod; } int main() { //freopen("c.out","w",stdout); scanf("%d%d",&n,&k); init(); for(int i=k;i<=n;i++) F(i); rongchi(); printf("%lld",ans); }