[数据丢失]已经没有什么好害怕的了

壹、题目描述 ¶

传送门 to Luogu.

贰、题解 ¶

无解情况:\(2\nmid (n+k)\),否则,令 \(x={n+k\over 2}\),即求刚好 \(x\) 组满足 \(a>b\) 的方案数。

但是恰好不是很好求,我们可以通过二项式反演将恰好转化为至少,现在我们想要得到至少 \(x\) 个合法配对的方案数,不难想到 \(\rm DP\).

我们不难设计状态 \(f(i, j)\) 表示前 \(i\) 个有至少 \(j\) 组是合法匹配,由于如果匹配了不合法对,他们会对后面的转移有影响,因为我们后面的转移必定要将前面的匹配断开,那么我们又要考虑断开的这一对的 \(a_i\) 去匹配哪一个 \(b_i\),然后......所以,我们只匹配不合法的对,其他的都是游离态,那么,我们不难得到转移式子:

\[f(i, j)=f(i-1, j)+f(i-1, j-1)\times t \]

这个 \(t\) 表示将 \(a_i\) 匹配到比它小的数的方案数。

现在有个问题,如果 \(a_i\) 并不是有序的,那么这个 \(t\) 很难进行处理,我们需要将 \(a\) 从小到大排序,这样 \(t=cnt_i-(j-1)\)\(cnt_i\) 表示在 \(b\) 中比 \(a_i\) 小的数的个数,那么

递推式:

\[f(i, j)=f(i-1, j)+(cnt_i-(j-1))\times f(i-1, j-1) \]

对于每个 \(f(n, j)\),还剩下 \(n-j\) 个没有配对,所以我们记 \(g(j)=(n-j)!f(n, j)\).

答案使用二项式反演:

\[Ans=\sum_{i=x}^n(-1)^{i-x}{i\choose x}g(i) \]

叁、参考代码 ¶

 #include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
#include<set>
using namespace std;

#define NDEBUG
#include<cassert>

namespace Elaina{
    #define rep(i, l, r) for(int i=(l), i##_end_=(r); i<=i##_end_; ++i)
    #define drep(i, l, r) for(int i=(l), i##_end_=(r); i>=i##_end_; --i)
    #define fi first
    #define se second
    #define mp(a, b) make_pair(a, b)
    #define Endl putchar('\n')
    #define mmset(a, b) memset(a, b, sizeof a)
    // #define int long long
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int, int> pii;
    typedef pair<ll, ll> pll;
    template<class T>inline T fab(T x){ return x<0? -x: x; }
    template<class T>inline void getmin(T& x, const T rhs){ x=min(x, rhs); }
    template<class T>inline void getmax(T& x, const T rhs){ x=max(x, rhs); }
    template<class T>inline T readin(T x){
        x=0; int f=0; char c;
        while((c=getchar())<'0' || '9'<c) if(c=='-') f=1;
        for(x=(c^48); '0'<=(c=getchar()) && c<='9'; x=(x<<1)+(x<<3)+(c^48));
        return f? -x: x;
    }
    template<class T>inline void writc(T x, char s='\n'){
        static int fwri_sta[1005], fwri_ed=0;
        if(x<0) putchar('-'), x=-x;
        do fwri_sta[++fwri_ed]=x%10, x/=10; while(x);
        while(putchar(fwri_sta[fwri_ed--]^48), fwri_ed);
        putchar(s);
    }
}
using namespace Elaina;

const int maxn=2000;
const int mod=1e9+9;

inline int qkpow(int a, int n){
    int ret=1;
    for(; n>0; n>>=1, a=1ll*a*a%mod)
        if(n&1) ret=1ll*ret*a%mod;
    return ret;
}
int fac[maxn+5], finv[maxn+5];
inline void prelude(){
    fac[0]=finv[0]=1;
    for(int i=1; i<=maxn; ++i)
        fac[i]=1ll*fac[i-1]*i%mod;
    finv[maxn]=qkpow(fac[maxn], mod-2);
    for(int i=maxn-1; i>=1; --i)
        finv[i]=1ll*finv[i+1]*(i+1)%mod;
}
inline int C(int n, int m){
    if(n<m) return 0;
    return 1ll*fac[n]*finv[m]%mod*finv[n-m]%mod;
}

int a[maxn+5], b[maxn+5], n, k;

inline void input(){
    n=readin(1), k=readin(1);
    rep(i, 1, n) a[i]=readin(1);
    rep(i, 1, n) b[i]=readin(1);
    sort(a+1, a+n+1), sort(b+1, b+n+1);
}

int f[maxn+5][maxn+5], g[maxn+5];
inline void getf(){
    f[0][0]=1;
    for(int i=1; i<=n; ++i){
        int cnt=0;
        for(int j=1; j<=n; ++j) if(b[j]<a[i])
            ++cnt;
        for(int j=0; j<=i; ++j){
            f[i][j]=f[i-1][j];
            if(j && cnt>=j-1) f[i][j]=(f[i][j]+1ll*(cnt-(j-1))*f[i-1][j-1]%mod)%mod;
        }
    }
    for(int j=1; j<=n; ++j)
        g[j]=1ll*f[n][j]*fac[n-j]%mod;
}

#define sign(i) (((i)&1)? -1: 1)
inline void getans(){
    int ans=0, x=(n+k)>>1;
    for(int i=x; i<=n; ++i){
        ans=(ans+mod+1ll*sign(i-x)*C(i, x)*g[i]%mod)%mod;
    }
    writc(ans);
}

signed main(){
    prelude();
    input();
    if((n+k)&1) return writc(0), 0;
    getf();
    getans();
    return 0;
}
posted @ 2021-08-06 10:14  Arextre  阅读(50)  评论(0编辑  收藏  举报