P4859 已经没有什么好害怕的了(二项式反演)

题目

思路

显然是恰好有\(\frac{n+k}{2}\)\(a>b\)

\(f(i,j)\)表示前\(i\)个糖果,已经有\(j\)\(a>b\),剩下的没管的方案数

\(a\)数组从小到大排序,设\(r_i\)表示比\(a_i\)小的\(b\)个数,那么\(r_i\)是递增的

有状态转移方程\(f(i,j) = f(i-1,j) + f(i-1,j-1) \times (r_i-j+1)\)

对于每个\(f(n,i)\),由于剩下的\(n-i\)对不知道大小情况,那么有\(f(n,i)\times (n-i)!\)种情况至少\(i\)个匹配

\(g_i\)表示恰好有\(i\)个匹配,那么\(f(n,i) \times (n-i)! = \Sigma_{j=i}^n C(j,i)\times g_i\)

二项式反演后就是\(g_i = \Sigma_{j=i}^n (-1)^{j-i}C(j,i)\times f(n,j)\times (n-j)!\)

#include<bits/stdc++.h>
#define N 2005 
using namespace std;
typedef long long ll;
const ll mod = 1000000009;
int n,k,a[N],b[N],r[N];
ll f[N][N];
ll fac[N],ifac[N];

template <class T> inline T Max(T a,T b) { return a > b ? a : b; }
template <class T> inline T Min(T a,T b) { return a < b ? a : b; }
template <class T> void read(T &x)
{
    char c;int sign=1;
    while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
    while((c=getchar())>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48; x*=sign;
}
ll qp(ll a,ll b) { ll ret=1; for(;b;b>>=1,a=a*a%mod) if(b&1) ret=ret*a%mod; return ret; }
ll C(int n,int m) { return n>=m ? fac[n]*ifac[m]%mod*ifac[n-m]%mod : 0; }
void init(int maxn)
{
    fac[0]=1; for(int i=1;i<=maxn;++i) fac[i]=fac[i-1]*i%mod;
    ifac[maxn]=qp(fac[maxn],mod-2);
    for(int i=maxn-1;i>=0;--i) ifac[i]=ifac[i+1]*(i+1)%mod; 
}
int main()
{
    read(n);read(k);
    for(int i=1;i<=n;++i) read(a[i]);
    for(int i=1;i<=n;++i) read(b[i]);
    sort(a+1,a+n+1); sort(b+1,b+n+1);
    if((n+k)&1) { printf("0"); return 0; }
    for(int i=1;i<=n;++i)
      for(int j=1;j<=n;++j)
        r[i]+=(a[i]>b[j]);
    init(2000);
    f[0][0]=1;
    for(int i=1;i<=n;++i)
    {
        f[i][0]=f[i-1][0];
        for(int j=1;j<=i;++j) f[i][j] = (f[i-1][j] + f[i-1][j-1] * max(0,r[i]-j+1))%mod;
    }
    ll ans=0;
    k=(n+k)/2;
    for(int i=k;i<=n;++i) ans = (ans + ((i-k)&1 ? -1 : 1) * C(i,k) * f[n][i]%mod *fac[n-i]%mod)%mod;
    printf("%lld\n",(ans%mod+mod)%mod);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Chtholly/p/11967407.html