有 \(a\) 和 \(b\) 两个长度为 \(n\) 的序列,其中元素两两配对, \(a>b\) 的配对需比 \(a<b\) 的配对多恰好 \(k\) 个,求方案数
我们设 \(f_{i,j}\) 为前 \(i\) 个 \(a\) 中,选了 \(j\) 组 \(a>b\) 的方案数
可得状态转移方程:
\(l_i\) 表示离 \(a_i\) 最近的一个 \(b\)
再设 \(g_i\) 为 \(\geq i\) 个的配对方案数, \(h_i\) 为恰好 \(=i\) 个的方案数,可得 \(g_i=f_{n,i}*(n-i)!\)
且容易得, \(f_i\) 在 \(g_j\) 中算了 \((^i_j)\) 次
所以,得到:
由二项式反演,得:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#define ll long long
using namespace std;
const int N=2005,mod=1e9+9;
int n,k,a[N],b[N];
ll f[N][N],fac[N],inv[N],fac_inv[N];
int g[N];
inline int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){if(ch==‘-‘) f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
return x*f;
}
inline void init(){
inv[1]=1;fac[0]=fac_inv[0]=1;
for(int i=1;i<=n;i++){
if(i!=1) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
fac[i]=fac[i-1]*i%mod;
fac_inv[i]=fac_inv[i-1]*inv[i]%mod;
}
}
inline ll C(int n,int m){
return fac[n]*fac_inv[m]%mod*fac_inv[n-m]%mod;
}
int main(){
n=read();k=read();
if((n+k)&1){
puts("0");
return 0;
}
k+=(n-k)/2;
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=n;i++) b[i]=read();
sort(a+1,a+n+1);
sort(b+1,b+n+1);
init();
int now=0;
for(int i=1;i<=n;i++){
while(now<n&&a[i]>b[now+1]) now++;
g[i]=now;
}
for(int i=0;i<=n;i++) f[i][0]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=g[i];j++)
f[i][j]=(f[i-1][j]+f[i-1][j-1]*(g[i]-j+1)%mod)%mod;
ll ans=0;
for(int i=k;i<=n;i++)
ans=(ans+(((i-k)&1)?-1:1)*fac[n-i]*f[n][i]%mod*C(i,k)%mod+mod)%mod;
printf("%lld\n",ans);
return 0;
}
原文:https://www.cnblogs.com/jasony/p/13641972.html