二项式反演(广义容斥定理)学习笔记

背景:

二项式反演又名广义容斥定理。
下次再看题解时要注意了。
解锁新姿势。
说白了就是之前会的姿势太少了。

正题:

说白了就是有这样两条恒等的式子:
f n = ∑ i = 0 n ( − 1 ) i C n i g i ⟺ g n = ∑ i = 0 n ( − 1 ) i C n i f i f_n=\sum_{i=0}^{n}(-1)^iC_{n}^{i}g_i⟺g_n=\sum_{i=0}^{n}(-1)^iC_{n}^{i}f_i fn=i=0n(1)iCnigign=i=0n(1)iCnifi

仔细思考一下,发现有:
f n = ∑ i = 0 n C n i g i ⟺ g n = ∑ i = 0 n ( − 1 ) n − i C n i f i f_n=\sum_{i=0}^{n}C_{n}^{i}g_i⟺g_n=\sum_{i=0}^{n}(-1)^{n-i}C_{n}^{i}f_i fn=i=0nCnigign=i=0n(1)niCnifi
证明:
感性一下就是一个容斥问题。
具体可见:http://blog.miskcoo.com/2015/12/inversion-magic-binomial-inversion

例题 1 1 1(恰好与至多的转换):

hdu P1465 \text{hdu P1465} hdu P1465:最不容易系列之一
que \text{que} que
n n n个数重新排序,求每一个数都不在原位置的方案数。

sol \text{sol} sol
这不就是一个错排问题吗? d 1 = 0 , d 2 = 1 , d n = ( n − 1 ) ( d n − 1 + d n − 2 ) d_1=0,d_2=1,d_n=(n-1)(d_{n-1}+d_{n-2}) d1=0,d2=1,dn=(n1)(dn1+dn2)
如果用二项式反演呢?
f i f_i fi表示至多 i i i个错开的方案数,有 f i = i ! f_i=i! fi=i!
g i g_i gi表示恰好 i i i个错开的方案数。
有:
f n = ∑ i = 0 n C n i g i f_n=\sum_{i=0}^{n}C_{n}^{i}g_i fn=i=0nCnigi

i i i枚举当前有几个错位)
反演一下,有:
g n = ∑ i = 0 n ( − 1 ) n − i C n i f i = ∑ i = 0 n ( − 1 ) n − i C n i i ! g_n=\sum_{i=0}^{n}(-1)^{n-i}C_{n}^{i}f_i=\sum_{i=0}^{n}(-1)^{n-i}C_{n}^{i}i! gn=i=0n(1)niCnifi=i=0n(1)niCnii!

code \text{code} code

就不打了吧。

例题 2 2 2(恰好与至少的转换):

luogu P4859 \text{luogu P4859} luogu P4859 已经没有什么好害怕的了
que \text{que} que
输入两个序列 A , B A,B A,B,保证两序列元素两两互不相同。这两个序列两两匹配,若当前配对的结果 A A A B B B大的组数 − B -B B A A A大的组数 = k =k =k,则方案加 1 1 1,问最后的方案。
sol \text{sol} sol
首先肯定是先对 A , B A,B A,B排序。
A A A B B B大的组数设为 x x x B B B A A A大的组数设为 y y y,那么有 x + y = n , x − y = k x+y=n,x-y=k x+y=n,xy=k,最后 x = n + k 2 x=\frac{n+k}{2} x=2n+k。我们记 k ′ = x = n + k 2 k'=x=\frac{n+k}{2} k=x=2n+k
容易发现,若 n + k n+k n+k为奇数,则无解。

d p i , j dp_{i,j} dpi,j表示前 i i i A A A中,选了至少 j j j a > b a>b a>b的方案数。
l a s t i last_i lasti表示 B l a s t i B_{last_i} Blasti恰好小于 A i A_i Ai,且 B l a s t i + 1 B_{last_i+1} Blasti+1恰好大于 A i A_i Ai
则有:
d p i , j = d p i − 1 , j + ( l a s t i − j + 1 ) d p i − 1 , j − 1 dp_{i,j}=dp_{i-1,j}+(last_i-j+1)dp_{i-1,j-1} dpi,j=dpi1,j+(lastij+1)dpi1,j1
因为选了 j j j个后剩下的数可以随意排列,因此最后的结果应该乘上 ( n − j ) ! (n-j)! (nj)!
f i f_i fi表示至少 i i i个大于的方案数,有 f i = ( n − i ) ! d p n , i f_i=(n-i)!dp_{n,i} fi=(ni)!dpn,i
g i g_i gi表示恰好 i i i个大于的方案数。
因此有:
f k ′ = ∑ i = k ′ n C i k ′ g i f_{k'}=\sum_{i=k'}^{n}C_{i}^{k'}g_i fk=i=knCikgi

i i i枚举当前有几个大于的)
反演一下,有:
g k ′ = ∑ i = k ′ n ( − 1 ) i − k ′ C i k ′ f i g_{k'}=\sum_{i=k'}^{n}(-1)^{i-k'}C_{i}^{k'}f_i gk=i=kn(1)ikCikfi

code \text{code} code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define mod 1000000009
using namespace std;
	int a[2010],b[2010],last[2010],fac[2010],inv[2010],dp[2010][2010],f[2010];
	int n,k;
	int ans=0;
void init(int n)
{
	fac[0]=fac[1]=1;
	inv[0]=inv[1]=1;
	for(int i=2;i<=n;i++)
	{
		fac[i]=(LL)fac[i-1]*i%mod;
		inv[i]=((LL)mod-mod/i)*inv[mod%i]%mod;
	}
	for(int i=2;i<=n;i++)
		inv[i]=(LL)inv[i-1]*inv[i]%mod;
}
int C(int n,int m)
{
	return (LL)fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
	scanf("%d %d",&n,&k);
	init(n);
	if((n+k)&1)
	{
		printf("0");
		return 0;
	}
	k=(n+k)>>1;
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++)
		scanf("%d",&b[i]);
	sort(b+1,b+n+1);
	int now=0;
	for(int i=1;i<=n;i++)
	{
		while(now<n&&b[now+1]<a[i]) now++;
		last[i]=now;
	}
	dp[0][0]=1;
	for(int i=1;i<=n;i++)
	{
		dp[i][0]=dp[i-1][0];
		for(int j=1;j<=i;j++)
			dp[i][j]=((LL)dp[i-1][j]+((LL)max(last[i]-j+1,0))*dp[i-1][j-1]%mod)%mod;
	}
	for(int i=1;i<=n;i++)
		f[i]=(LL)fac[n-i]*dp[n][i]%mod;
	for(int i=1;i<=n;i++)
		ans=(ans+((((i-k)&1)?-1ll:1ll)*C(i,k)*f[i]%mod+mod)%mod)%mod;
	printf("%d",ans);
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值