容斥原理
对于对象有重叠的问题,先不考虑重叠的情况,把包含于某内容中的所有对象的数目先计算出来,然后再把计数时重复计算的数目排斥出去,使得计算的结果既无遗漏又无重复,这种计数的方法称为容斥原理。
容斥原理的原则:奇加偶减。
推广:
其中 为所有 的集合。
DeMorgan定理
推广:
放石子(NKOJ 3495)
问题描述
在一个 的矩形网格里放 个相同的石子,问有多少种方法?每个格子最多放一个石子,所有石子都要放完,并且第一行、最后一行、第一列、最后一列都得有石子。
输入格式
三个整数 。
输出格式
一个整数,表示所求方法数。
结果可能很大, 后再输出。
样例输入
2 3 4
样例输出
13
提示
表示第
行不放石子的方案集合,
表示第
行不放石子的方案集合,
表示第
列不放石子的方案集合,
表示第
列不放石子的方案集合。设全集为
,答案为在
中而不在
中的所有方案,即
然后用容斥原理和组合数计算。式子特别长。
typedef long long LL;
const LL pmod=1000007;
LL C[500][500];
void make_C(LL n){
for(LL i=0;i<=n;i++)C[i][0]=C[i][i]=1;
for(LL i=0;i<=n;i++)for(LL j=1;j<i;j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%pmod;
}
int main(){
LL n,m,k;read(&n,&m,&k);
make_C(n*m);
LL S,A,B,L,R,AB,AL,AR,BL,BR,LR,ABL,ABR,ALR,BLR,ABLR,M;
S=C[n*m][k];
A=B=C[n*(m-1)][k];
L=R=C[(n-1)*m][k];
AB=C[n*(m-2)][k];
LR=C[(n-2)*m][k];
AL=AR=BL=BR=C[(n-1)*(m-1)][k];
ABL=ABR=C[(n-2)*(m-1)][k];
ALR=BLR=C[(n-1)*(m-2)][k];
ABLR=C[(n-2)*(m-2)][k];
M=A+B+L+R-AB-AL-AR-BL-BR-LR+ABL+ABR+ALR+BLR-ABLR;
write(k?((S-M)%pmod+pmod)%pmod:1,"\n");
return 0;
}
集合计数(NKOJ 4487)
问题描述
一个有 个元素的集合有 个不同子集(包含空集),现在要在这 个集合中取出若干集合(至少一个),使得它们的交集的元素个数为 ,求取法的方案数,答案模 。
输入格式
一行两个整数 。
输出格式
一行为答案。
样例输入
3 2
样例输出
6
提示
先确定交集中的
个元素,方案数为
。
那么需要在剩余的
个元素里取出若干集合,使得这些集合没有交集。记方案数为
。
记
为在
元集合中取出若干子集(至少一个)的方案数。(取空集的方案包含在取一个子集的方案内。)
根据定义,
对
,
就表示在剩余的
个元素里选出若干集合,使得交集中至少有
个元素的方案数。其中
确定交集中一定含有的
个元素,
统计在另外
个元素中选取集合的方案数,在这些集合中各添加确定的
个元素即为所求方案。
根据奇加偶减的原则,
所以
typedef long long LL;
const LL pmod=1000000007;
const int maxn=1000000;
LL C[maxn];
LL Pow(LL a,LL b,LL c){
LL ans=1;a=a%c;
while(b){
if(b%2)ans=(ans*a)%c;
b=b/2,a=(a*a)%c;
}
return ans;
}
void make_Cn(LL n){
C[0]=1,C[1]=n;
for(LL i=2;i<=n;i++)
C[i]=((C[i-1]*(n-i+1))%pmod*Pow(i,pmod-2,pmod))%pmod;
}
int main(){
LL n,k,ans,t=0;read(&n,&k);
make_Cn(n);ans=C[k];
make_Cn(n-k);
for(LL i=0;i<=n-k;i++)
t=(t+(((i&1?-1:1)*C[i])%pmod*(Pow(2,Pow(2,n-k-i,pmod-1),pmod)-1))%pmod+pmod)%pmod;
ans=(ans*t)%pmod;
write(ans,"\n");
return 0;
}