容斥

容斥

Devu and Flowers

题意
\(n\) 种花,每种 \(f_i\) 朵。求选择 \(s\) 朵方案。答案对 \(1e9+7\)取模。\(1\leq n\leq 20,0\leq f_i\leq 10^{12},0\leq s\leq 10^{14}\)
思路:插板法+容斥。因为可能选 \(0\) 朵,所以不能直接用插板法,我们可以整体加\(n\),表示每个都先选了1朵,这样再来求。
code:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int mod=1e9+7;
ll n,a[22],m,ans;
ll inv[22];
ll ksm(ll x,ll p){
	ll as=1;
	while(p){
		if(p&1){
			as=(as*x)%mod;
		}
		x=(x*x)%mod;
		p>>=1;
	}
	return as;
}
ll C(ll x,ll y){
	ll s=1,fm=1;
	if(x>y||x<0||y<0) return 0;
	if(y==0||x==0) return 1;
	y%=mod;
	y+=mod;
	for(ll i=y;i>=y-x+1;i--){
		s=(s*i)%mod;
	}
//	cout<<s<<endl;
	for(ll i=1;i<=x;i++){
		fm=(fm*inv[i])%mod;
	}
//	fm=ksm(fm,mod-2);
	s=(s*fm)%mod;
	return s;
}
void init(){
	for(ll i=1;i<=20;i++){
		inv[i]=ksm(i,mod-2);
	}
}
int main(){
//	cout<<C(1,5);
	init();
	scanf("%lld%lld",&n,&m);
	ans=C(n-1,n+m-1);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	for(int i=1;i<(1<<n);i++){
		int fl=0;
		ll sum=(m+n-1);
		for(int j=1;j<=n;j++){
			if((i>>(j-1))&1){
				sum=sum-(a[j]+1);
				fl^=1;
			}
		}
	//	cout<<ans<<endl;
		sum=sum%mod;
		if(fl==0) ans=(ans+C(n-1,sum))%mod;
		else ans=((ans-C(n-1,sum))%mod+mod)%mod;
	}
	printf("%lld\n",ans);
	return 0;
}

启示

  1. 求组合数时若 \(n\)\(m\) 大,可以直接枚举来计算。
  2. 若要求可以不选时的插板法,可以把整体都加 \(n\),表示每个选了一个,再继续操作。

CF348D Turtles

题意\(n\times m\) 网格图,图中有些格子上有障碍物,两只乌龟从 \((1,1)\) 走到 \((n,m)\),可以向下或向右,求两只乌龟不交路径对数,答案对 \(1e9+7\) 取模。\(n,m\leq3000\)
思路:一只乌龟的很简单,设 \(f_{i,j}\) 表示走到 \((i,j)\) 的路径数,那么 \(f_{i,j}=[a_{i,j}=="."](f_{i,j}+f_{i-1,j}+f_{i,j-1})\)
怎么判断不交呢?我们可以先看有哪些是一定相交的。首先,两只乌龟一定分别经过 \((1,2)\)\((n-1,m)\),\((2,1)\)\((n,m-1)\)。若两条路径同时走了 \((1,2)\)\((n,m-1)\),\((2,1)\)\((n-1,m)\),那就是有交。
code:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=3002;
const int mod=1e9+7;
ll f[N][N];
int a[N][N];
int n,m;
ll a1,a2,a3,a4;
ll solve(int bx,int by,int ex,int ey){
	for(int i=0;i<=n;i++){
		for(int j=0;j<=m;j++) f[i][j]=0;
	}
	f[bx][by]=1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(a[i][j]==0) f[i][j]=0;
			else f[i][j]=((f[i][j]+f[i-1][j])%mod+f[i][j-1])%mod;
		}
	}
	return f[ex][ey];
}
int main(){
	char s;
	scanf("%d%d",&n,&m);
	s=getchar();
	for(int i=1;i<=n;i++){
		for(int j=0;j<m;j++){
			s=getchar();
			if(s=='.') a[i][j+1]=1;
			else a[i][j+1]=0;
		}
		s=getchar();
	}
	a1=solve(1,2,n-1,m);
	a2=solve(2,1,n,m-1);
	a3=solve(1,2,n,m-1);
	a4=solve(2,1,n-1,m);
	printf("%lld\n",((a1*a2%mod-a3*a4%mod)%mod+mod)%mod);
	return 0;
}

启示

  1. cin被卡的时候可以换成getchar,注意输入换行。
  2. lgv引理。对于路径不相交问题,试着对它们的起点终点进行匹配。
posted @ 2025-05-25 21:53  MoYujing  阅读(13)  评论(0)    收藏  举报