码迷,mamicode.com
首页 > 其他好文 > 详细

[bzoj3622]已经没有什么好害怕的了——容斥or二项式反演+DP

时间:2019-01-05 12:06:39      阅读:181      评论:0      收藏:0      [点我收藏+]

标签:没有   push   spl   mes   turn   决策   tchar   end   name   

题目大意:

给定两个长度为\(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}\times (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}\times g_{j}\\]
根据二项式反演可得:
\[ g_{i}=\sum_{j=i}^{n}(-1)^{j-i}\times {j\choose i}\times f_j \]
直接\(O(n)\)反演即可。
当然观察到\(f_i\)\(g_i\)的关系之后,我们可以倒推利用容斥来推出\(g_i\),此时可得:
\[ g_i=f_i-\sum_{j=i+1}^{n}{j\choose 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\n",(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\n",(g[m]+mod)%mod);

    return 0;
}

[bzoj3622]已经没有什么好害怕的了——容斥or二项式反演+DP

标签:没有   push   spl   mes   turn   决策   tchar   end   name   

原文地址:https://www.cnblogs.com/ylsoi/p/10223764.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!