BZOJ 3622: 已经没有什么好害怕的了 组合数+Dp

title

BZOJ 3622

LUOGU 4859

简化题意:

两个数列 \(\{a_n\}\)\(\{b_n\}\),求 \(a_i>b_i\) 的组数恰好比 \(b_i>a_i\) 的组数多 \(k\) 组的情况个数。

analysis

可以转化一下题目所求,也是为了方便:

\(k=\frac{n+k}{2}\),即有恰好 \(k\) 组糖果比药片大(\(a_i>b_i\))。

显然要将 \(a_i,b_i\) 从小到大排序。

考虑 \(a_i\) 造成的影响:

  1. \(a_i\) 匹配了 \(b,a_i>b\),则对于 \(a_j,j>i\),它匹配的 \(b,a_j>b\) 的方案数少了 1;
  2. \(a_i\) 匹配了 \(b,a_i<b\)……似乎又要分类讨论,状态很难记录。

所以,我们 \(Dp\) 时先考虑第一种的 \(a_i\) ,第二种的最后统一分配。

\(g_{i,j}\) 表示前 \(i\)\(a\),有 \(j\) 个第一种的方案数

状态转移方程为:
\[ g_{i,j}=g_{i-1,j}+(r_i-(j-1))g_{i-1,j-1}(r_i表示b中小于a_i的个数) \]
然后,我们可以令
\[ f_i=(n-i)!g_{n,i} \]
也就是把 \(n-i\) 个没有匹配的任意分配,得到至少 \(i\) 个的答案 \(f_i\)

那么恰好 \(i\) 个的答案可以从大往小递推得到:
\[ ans_i=f_i-\sum^{n}_{j=i+1}C^i_jans_j \]

code

#include<bits/stdc++.h>

typedef long long ll;
const int maxn=2110,mod=1e9+9;

namespace IO
{
    char buf[1<<15],*fs,*ft;
    inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
    template<typename T>inline void read(T &x)
    {
        x=0;
        T f=1, ch=getchar();
        while (!isdigit(ch) && ch^'-') ch=getchar();
        if (ch=='-') f=-1, ch=getchar();
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }

    char Out[1<<24],*fe=Out;
    inline void flush() { fwrite(Out,1,fe-Out,stdout); fe=Out; }
    template<typename T>inline void write(T x)
    {
        if (!x) *fe++=48;
        if (x<0) *fe++='-', x=-x;
        T num=0, ch[20];
        while (x) ch[++num]=x%10+48, x/=10;
        while (num) *fe++=ch[num--];
        *fe++='\n';
    }
}

using IO::read;
using IO::write;

inline ll Quick_power(ll a,ll b)
{
    ll ans=1;
    while (b)
    {
        if (b&1) ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}

ll fac[maxn],inv[maxn];
inline ll C(int n,int m)
{
    if (m==-1) return n==-1;
    return fac[n]*inv[m]%mod*inv[n-m]%mod;
}

int a[maxn],b[maxn],r[maxn];
ll g[maxn][maxn],f[maxn],ans[maxn];
int main()
{
    int n,K;read(n);read(K);
    fac[0]=1;
    for (int i=1; i<=maxn-1; ++i) fac[i]=fac[i-1]*i%mod;
    inv[maxn-1]=Quick_power(fac[maxn-1],mod-2);
    for (int i=maxn-2; i>=0; --i) inv[i]=inv[i+1]*(i+1)%mod;

    if ((n+K)&1) return puts("0"),0;
    K=(n+K)>>1;
    for (int i=1; i<=n; ++i) read(a[i]);
    for (int i=1; i<=n; ++i) read(b[i]);
    std::sort(a+1,a+n+1);
    std::sort(b+1,b+n+1);

    for (int i=1, c=0; i<=n; ++i)
    {
        while (c<n && b[c+1]<a[i]) ++c;
        r[i]=c;
    }

    g[0][0]=1;
    for (int i=1; i<=n; ++i)
        for (int j=0; j<=i; ++j) g[i][j]=(g[i-1][j]+(j ? 1ll*(r[i]-j+1)*g[i-1][j-1]%mod : 0ll))%mod;
    for (int i=0; i<=n; ++i) f[i]=g[n][i]*fac[n-i]%mod;
    for (int i=n; i>=K; --i)
    {
        ans[i]=f[i];
        for (int j=i+1; j<=n; ++j) ans[i]=(ans[i]-ans[j]*C(j,i)%mod+mod)%mod;
    }
    write(ans[K]);
    IO::flush();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/G-hsm/p/11374121.html