zoukankan      html  css  js  c++  java
  • 二项式反演笔记

    二项式反演

    [f(n)=sum^{n}_{i=0} g(i) C_{n}^{i} ]

    [g(n)=sum^{n}_{i=0}(-1)^{n-i}f(i)C_{n}^{i} ]

    证明:

    代入(g(i))得:

    [g(n)=sum^{n}_{i=0}(-1)^{n-i}C_{n}^{i}sum_{j=0}^{i}g(j)C_{i}^{j} ]

    改变枚举顺序可得:

    [g(n)=sum^{n}_{i=0}(-1)^{n-i}C_{n}^{i}C_{i}^{j}sum_{j=0}^{i}g(j) ]

    [g(n)=sum_{j=0}^{n}g(j)sum^{n}_{i=0}(-1)^{n-i}C_{n}^{i}C_{i}^{j} ]

    我们可以通过暴力得到一个式子:

    [C_{n}^{i}C_{i}^{j}= frac{n!}{i!(n-i)!} imesfrac{i!}{j!(i-j)!}=frac{n!}{j!(n-j)!} imesfrac{(n-j)!}{(n-i)!(i-j)!}=C^{j}_{n}C^{n-i}_{n-j} ]

    把这个式子代入得:

    [g(n)=sum_{j=0}^{n}g(j)sum^{n}_{i=0}(-1)^{n-i}C^{j}_{n}C^{n-i}_{n-j} ]

    所以

    [g(n)=sum_{j=0}^{n}g(j)C^{j}_{n}sum^{n}_{i=0}(-1)^{n-i}C^{n-i}_{n-j} ]

    (i e n)时,由二项式定理得:

    [sum^{n}_{i=0}(-1)^{n-i}C^{n-i}_{n-j}=(-1+1)^{n-j}=0 ]

    所以(此时(i= n)):

    [g(n)=g(n)C^{n}_{n}=g(n) ]

    证毕

    例题

    洛谷P4859 已经没有什么好害怕的了

    由题意得,(k=frac{n+k}{2}) (和差问题)

    我们首先先将(a,b)从小到大排序

    (f[i][j])表示前(i)(a)中,选了(j)(a)使得(a>b) 得方案数

    (last[i])表示(b)中最后一个(<a[i])的数的编号

    无非就是选和不选,于是我们很容易得出状态转移方程:

    [f[i][j]=f[i-1][j]+(last[i]-j+1)*f[i-1][j-1] ]

    这里我们遇见了一个二项式反演题目中得一个常见套路:至少恰好

    (g[i])表示至少选了(i)个的方案数,(ans[i])表示恰好选了(i)的方案数(就是我们要的答案)

    在先前的dp中,我们还剩下一些没有选的数,它们可以任意排列

    于是:

    [g[i]=f[n][i]*(n-i)! ]

    对于每个(g[i]),我们试图寻找它和所有比(i)大的(j)(ans[j])的关系

    对于每个(ans[j])中的每一种方案,我们可以从所有(j)个人中任意选择若干个人

    所以我们得到:

    [g[i]=sum_{j=i}^{n}C_{j}^{i}ans[j] ]

    由二项式反演得:

    [ans[j]=sum_{j=i}^{n}(-1)^{j-i}C^{i}_{j}g[j] ]

    代码被我Gu掉了)

    #include<bits/stdc++.h>
    #define int long long
    #define MOD 1000000009
    using namespace std;
    const int maxn=2e3+5;
    int n,k;
    int a[maxn];
    int b[maxn];
    int dp[maxn][maxn];
    int last[maxn];
    int f[maxn];
    int invf[maxn];
    int g[maxn];
    int ans[maxn];
    inline int ksm(int x,int y){
    	if(!y) return 1;
    	if(y==1) return x%MOD;
    	int tmp=ksm(x,y/2)%MOD;
    	tmp=(1ll*tmp*tmp)%MOD;
    	if(y&1) return (tmp*x)%MOD;
    	else return tmp;
    }
    inline int calc(int x,int y){
    	return 1ll*f[x]*invf[y]%MOD*invf[x-y]%MOD;
    }
    signed main(){
    	scanf("%lld%lld",&n,&k);
    	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    	for(int i=1;i<=n;i++) scanf("%lld",&b[i]);
    	if((n+k)&1){
    		printf("0
    ");
    		return 0;
    	}
    	sort(a+1,a+1+n);
    	sort(b+1,b+1+n);
    	f[0]=1;
    	invf[0]=1;
    	for(int i=1;i<=n;i++) f[i]=(1ll*f[i-1]*i)%MOD;
    	invf[n]=ksm(f[n],MOD-2);
    	for(int i=n-1;i>=1;i--) invf[i]=(1ll*invf[i+1]*(i+1))%MOD;
    	k=(k+n)/2;
    	for(int i=1;i<=n;i++) last[i]=lower_bound(b+1,b+1+n,a[i])-b-1;
    	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]=(1ll*dp[i][j]+(dp[i-1][j]+(1ll*dp[i-1][j-1]*max(0ll,last[i]-j+1))%MOD)%MOD)%MOD;
    	}
    	for(int i=1;i<=n;i++)	
    		g[i]=(1ll*dp[n][i]*f[n-i])%MOD;
    	int ans=0;
    	for(int i=k;i<=n;i++)	
    		if((i-k)&1) ans-=(1ll*calc(i,k)*g[i])%MOD,ans%=MOD;
    		else ans+=(1ll*calc(i,k)*g[i])%MOD,ans%=MOD;
    	while(ans<0) ans+=MOD;
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    CentOS 7.X 关闭SELinux
    删除或重命名文件夹和文件的方法
    centos7-每天定时备份 mysql数据库
    centos7 tar.gz zip 解压命令
    MySQL5.6/5.7/8.0版本授权用户远程连接
    下载CentOS7系统
    使用js实现tab页签切换效果
    sql优化常用的几种方法
    mysql 多表联查的快速查询(索引)
    【图论】强连通分量+tarjan算法
  • 原文地址:https://www.cnblogs.com/ybwowen/p/10963195.html
Copyright © 2011-2022 走看看