讲解
形式1:
形式2:
证明方法可以参考这篇博客:二项式反演及其应用 - GXZlegend - 博客园
证明思路大概就是把g(n)=....代入到左边的式子去,然后进行sigma前后交换、组合数交变、适时提取共有项、出现"sigma+0"消掉、最后根据定义得到了答案. 大概是这样一个证明思路f(n)=....g(i)=....f(i)=.......#=f(n)。
二项式反演其实就是一种容斥。不过是更加模板化的容斥。
在遇到恰好问题不好解决时,就可以考虑解决至少、至多的问题,这个问题解决就是f(n)的值。一般是会比较好求的,用点数学方法或者DP就可以解决。然后,用二项式反演,求出g(n)。这就是恰好的方案数了。
一般都是这么一个思路。
例题依旧可以参考这篇博客:二项式反演及其应用 - GXZlegend - 博客园。
解释一下“钦定k个”是个啥意思。就是先选定k个让它们一定成立,然后剩下n-k个就随意它们组合,那可以预见,最终结果可能会出现,成立的不只是刚开始选的那k个,还在随机组合过程中多成立了一些。所以才有“至少”、“至多”、“恰好”这一说。这便是钦定,用理科生的概括来说,就是:选定k一定成立,剩下随机。
洛谷迷给出洛谷的提交代码方法,嘻嘻
例题1
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD=1e9+7;
const int N=1e3+10;
ll C[2*N][2*N];
int pre_C(int n)
{
C[0][0]=1;
for(int i=1;i<=n;i++)
{
C[i][0]=1;
for(int j=1;j<=i;j++)
{
ll tmp=C[i-1][j-1]+C[i-1][j];
C[i][j]=tmp>=MOD?tmp-MOD:tmp;
}
}
}
int a[N];
int main()
{
int n,m;
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++) scanf("%d",&a[i]);
pre_C(2000);
ll ans=0;
for(int i=0;i<=n;i++)
{
ll tmp=C[n][i];
for(int j=1;j<=m;j++) tmp=tmp*C[a[j]+n-i-1][a[j]]%MOD;
ans=(ans+(i&1?-tmp:tmp)+MOD)%MOD;
}
printf("%lld",ans);
return 0;
}
例题2
这题难倒我的是DP。。。
显然m=(n+k)/2为需求A>B的个数;A,B需要排序。
dp[i][j]表示前i个中,钦定j组的方案数。
cnt[i]表示for(int j=1;j<=n;j++) cnt[i]+=a[i]>b[j];
把dp[i][0]=1, 初始化就ok了.
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD=1e9+9;
const int N=2e3+10;
ll C[N][N];
int pre_C(int n)
{
C[0][0]=1;
for(int i=1;i<=n;i++)
{
C[i][0]=1;
for(int j=1;j<=i;j++)
{
ll tmp=C[i-1][j-1]+C[i-1][j];
C[i][j]=tmp>=MOD?tmp-MOD:tmp;
}
}
}
int a[N],b[N];
ll dp[N][N];int cnt[N],pre[N];
ll fact[N];
int main()
{
int n,m,k;
scanf("%d %d",&n,&k);m=(n+k)/2;
if((n+k)&1)
{
puts("0");
return 0;
}
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
sort(a+1,a+n+1);
sort(b+1,b+n+1);
pre_C(n);
int p=0;
for(int i = 0 ; i <= n ; i ++ ) dp[i][0] = 1;
for(int i = 1 ; i <= n ; i ++ )
{
while(p < n && b[p + 1] < a[i]) p ++ ;
for(int j = 1 ; j <= n ; j ++ )
dp[i][j] = (dp[i - 1][j] + dp[i - 1][j - 1] * (p - j + 1)) % MOD;
}
fact[1]=1;
for(int i=2;i<=n-m;i++) fact[i]=fact[i-1]*i%MOD;
ll ans=0;
for(int i=m;i<=n;i++)
{
ll tmp=C[i][m]*dp[n][i]%MOD*fact[n-i]%MOD;
if((i-m)&1) ans=(ans-tmp+MOD)%MOD;
else ans=(ans+tmp)%MOD;
}
printf("%lld",ans);
return 0;
}