1. 程式人生 > >【BZOJ2839】集合計數 組合數+容斥

【BZOJ2839】集合計數 組合數+容斥

什麽 can sample 整數 ips out highlight lag -1

【BZOJ2839】集合計數

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}
【數據說明】
對於100%的數據,1≤N≤1000000;0≤K≤N;

題解:容斥,考慮選出若幹集合使得交集至少為k的方案數,有$f(i)=C _n^i \times (2^{2^{n-i}}-1)$,可以理解為已經選定了i個,剩下$2^{n-i}$個集合,每個可以選或不選,但是不能一個也不選。但是這樣做肯定會有重復的,我們思考容斥系數是什麽。

當我們計算交集至少為k的時候,每個交集為j的方案都會被計算$C_j^k$次,所以
f(k)的系數是1
f(k+1)的系數是$-C_{k+1}^k$
f(k+2)的系數$-C_{k+2}^k+C_{k+1}^kC_{k+2}^{k+1}=C_{k+2}^k$(小tips:$C_N^MC_M^S=C_N^SC_{N-S}^{N-M}$)

以此類推,f(i)的系數就是$(-1)^{i-k}C_i^k$。

所以答案為$\sum\limits_{i=k}^n(-1)^{i-k}C_i^kC_n^i(2^{2^{n-i}}-1)$

求組合數需要線性篩逆元,方法:$i^{-1}\equiv\lfloor{p\over i}\rfloor\times(p\%i)^{-1}(\mod p)$

求$(2^{2^i}-1)$可以采用從n到k枚舉i的方法,初值tmp=1,然後tmp=tmp*(tmp+2)。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const ll mod=1000000007;
ll n,k,ans;
ll ine[1000010],jcc[1000010],jc[1000010];
ll c(ll x,ll y)
{
	return jc[x]*jcc[y]%mod*jcc[x-y]%mod;
}
int main()
{
	scanf("%lld%lld",&n,&k);
	ll i,j,flag,tmp;
	ine[1]=jc[1]=jcc[1]=jc[0]=jcc[0]=1;
	for(i=2;i<=n;i++)
	{
		ine[i]=(mod-(mod/i)*ine[mod%i])%mod;
		jcc[i]=jcc[i-1]*ine[i]%mod;
		jc[i]=jc[i-1]*i%mod;
	}
	for(i=n,flag=((n-k)&1)?-1:1,tmp=1;i>=k;i--)
	{
		ans=(ans+mod+flag*c(i,k)*c(n,i)%mod*tmp%mod)%mod;
		flag=-flag,tmp=tmp*(tmp+2)%mod;
	}
	printf("%lld",ans);
	return 0;
}

【BZOJ2839】集合計數 組合數+容斥