zoukankan      html  css  js  c++  java
  • [bzoj3622]已经没有什么好害怕的了——容斥or二项式反演+DP

    题目大意:

    给定两个长度为(n)的序列,求有多少种匹配方式,使得(a_i<b_i)的个数恰好为(k)个。

    思路:

    据说是一道二项式反演的经典例题了。
    首先如果要求正好等于(k)个的是不太好求的,我们可以考虑求出至少为(k)个的方案数。
    首先先把两个序列都按照从小到大的顺序排好序,然后以序列(b)为对象dp。
    我们设(f_{i,j})表示前(i)个数里面强制确定了(j)(a_i<b_i)关系的方案数,记(c_i)表示在(a)中有多少个数<(b_i),于是可以得到方程

    [f_{i,j}=f_{i-1,j}+f_{i-1,j-1} imes (c_{i}-j+1)\ ]

    由于排了序的缘故,第(i)个数做出的决策一定有(c_i-j+1)种。
    当然,最后(f_{n,i})还需要乘以((n-i)!)
    这时我们可以得到最后的答案({g})(f_{n,i})的关系为:

    [f_{i}=sum_{j=i}^{n}{j choose i} imes g_{j}\ ]

    根据二项式反演可得:

    [g_{i}=sum_{j=i}^{n}(-1)^{j-i} imes {jchoose i} imes f_j ]

    直接(O(n))反演即可。
    当然观察到(f_i)(g_i)的关系之后,我们可以倒推利用容斥来推出(g_i),此时可得:

    [g_i=f_i-sum_{j=i+1}^{n}{jchoose i}g_j ]

    这也不失为一种很好的理解方法。

    #include<bits/stdc++.h>
    
    #define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
    #define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
    #define debug(x) cout<<#x<<"="<<x<<" "
    #define fi first
    #define se second
    #define mk make_pair
    #define pb push_back
    typedef long long ll;
    
    using namespace std;
    
    void File(){
    	freopen("bzoj3622.in","r",stdin);
    	freopen("bzoj3622.out","w",stdout);
    }
    
    template<typename T>void read(T &_){
    	_=0; T f=1; char c=getchar();
    	for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    	for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
    	_*=f;
    }
    
    const ll mod=1e9+9;
    const int maxn=2000+10;
    int n,m,a[maxn],b[maxn];
    ll fac[maxn],ifac[maxn],c[maxn],f[maxn][maxn],g[maxn],ans;
    
    ll qpow(ll x,ll y){
    	ll ret=1; x%=mod;
    	while(y){
    		if(y&1)ret=ret*x%mod;
    		x=x*x%mod;
    		y>>=1;
    	}
    	return ret;
    }
    
    void ad(ll &_,ll __){_=(_+__)%mod;}
    
    ll C(int x,int y){
    	return fac[x]*ifac[y]%mod*ifac[x-y]%mod;
    }
    
    int main(){
    	File();
    	read(n),read(m);
    	REP(i,1,n)read(a[i]);
    	REP(i,1,n)read(b[i]);
    
    	if((n-m)%2)return puts("0"),0;
    	m=(n-m)/2;
    
    	fac[0]=1;
    	REP(i,1,n)fac[i]=fac[i-1]*i%mod;
    	ifac[n]=qpow(fac[n],mod-2);
    	DREP(i,n-1,0)ifac[i]=ifac[i+1]*(i+1)%mod;
    
    	sort(a+1,a+n+1);
    	sort(b+1,b+n+1);
    
    	int p=0;
    	REP(i,1,n){
    		while(p<n && a[p+1]<b[i])++p;
    		c[i]=p;
    	}
    
    	f[0][0]=1;
    	REP(i,1,n)REP(j,0,i){
    		ad(f[i][j],f[i-1][j]);
    		if(j)ad(f[i][j],f[i-1][j-1]*(c[i]-j+1)%mod);
    	}
    
    	REP(i,0,n)f[n][i]=f[n][i]*fac[n-i]%mod;
    
    	/*REP(i,m,n)ad(ans,((i-m)%2 ? -1 : 1)*C(i,m)*f[n][i]%mod);
    	printf("%lld
    ",(ans+mod)%mod);*/
    
    	DREP(i,n,m){
    		g[i]=f[n][i];
    		REP(j,i+1,n)ad(g[i],-C(j,i)*g[j]%mod);
    	}
    
    	printf("%lld
    ",(g[m]+mod)%mod);
    
    	return 0;
    }
    
    
  • 相关阅读:
    Phonegap中用ajax读取PHP服务器的数据
    verilog中的有符号数理解(转)
    接口,抽象类的理解(转载)
    java中static关键字的理解(转载)
    关于码间串扰(转载)
    恒参信道对信号传输的影响
    多径随参信道对信号传输的影响
    转载——子空间投影的角度理解最小二乘
    转载——关于bp神经网络
    imsl库的使用过程中遇到的问题
  • 原文地址:https://www.cnblogs.com/ylsoi/p/10223764.html
Copyright © 2011-2022 走看看