前置知识:二项式反演
problem
两个 \(n\leq 2000\) 的数组 \(A,B\),元素互不相同,求有多少种将 \(A,B\) 配对的方法使得 \(A>B\) 的恰好有 \(k\) 对。
题目改过,但是这一步转换很简单。
solution
恰好很难算。我们算至少:\(g(t)\) 表示钦定 \(t\) 个 pair 是大于有多少种方案,\(f(t)\) 表示恰好有 \(t\) 个 pair 是大于有多少种方案。
二项式反演:
\[g(k)=\sum_{k\leq i\leq n}\binom{i}{k}f(i)\Leftrightarrow f(k)=\sum_{k\leq i\leq n}(-1)^{i-k}\binom{i}{k}g(i). \]但是这个题的难点不在这里,我们要算 \(g\),怎么算呢?DP。
首先 \(A,B\) 排序对结果无影响,然后二分求出 \(h_i=\sum_j[b_j<a_i]\) 表示有多少个 \(b\) 小于 \(a\)。
令 \(f_{i,j}\) 表示考虑到 \(a_i\),前面有 \(j\) 个已经匹配。
- \(i\) 不匹配:\(f_{i-1,j}\)。
- \(i\) 匹配:\((h_i-j+1)f_{i-1,j-1}\),这表示 \(i\) 的匹配对象中,\(j-1\) 个已经被用了,其他的随便挑一个。
然后剩下的随机匹配,得到 \(g(t)=f_{n,j}(n-j)!\)。
code
点击查看代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
const int P=1e9+9;
LL mod(LL x){return (x%P+P)%P;}
void red(LL&x){x=mod(x);}
LL qpow(LL a,LL b,int p=P){LL r=1;for(a%=p;b;b>>=1,a=a*a%p) if(b&1) r=r*a%p; return r;}
template<int N,int P> struct C_prime{
LL fac[N+10],ifac[N+10];
C_prime(){
for(int i=fac[0]=ifac[0]=1;i<=N;i++) fac[i]=fac[i-1]*i%P;
ifac[N]=qpow(fac[N],P-2,P);
for(int i=N-1;i>=1;i--) ifac[i]=ifac[i+1]*(i+1)%P;
}
LL operator()(int n,int m){return n>=m?fac[n]*ifac[n-m]%P*ifac[m]%P:0;}
};
int n,k,a[2010],b[2010],h[2010],g[2010];
LL f[2010][2010],fac[2010],ans;
C_prime<2010,P> C;
void dp(){
f[0][0]=1;
for(int i=1;i<=n;i++){
for(int j=0;j<=i;j++){
f[i][j]=f[i-1][j];
if(j&&h[i]>=j-1) red(f[i][j]+=f[i-1][j-1]*(h[i]-j+1));
}
}
for(int j=0;j<=n;j++) g[j]=f[n][j]*fac[n-j]%P;
}
int main(){
for(int i=fac[0]=1;i<=2000;i++) fac[i]=fac[i-1]*i%P;
// #ifdef LOCAL
// freopen("input.in","r",stdin);
// #endif
scanf("%d%d",&n,&k);
if((n+k)&1) return puts("0"),0;
k=(n+k)/2;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
sort(a+1,a+n+1),sort(b+1,b+n+1);
for(int i=1;i<=n;i++) h[i]=lower_bound(b+1,b+n+1,a[i])-b-1;
dp();
for(int i=k;i<=n;i++) red(ans+=C(i,k)*((i-k)&1?-1:1)*g[i]);
printf("%lld\n",ans);
return 0;
}
//2x-n=k => x=(n+k)/2
//f[i][j]=f[i-1][j]+f[i-1][j-1]*(h[i]-(j-1))