大意:
给定两个数组a,b,要求对两个数组中的元素两两配对,使得恰好有ai>bi的组数恰好比ai<bi的组数多k对。问方案数
数据保证所有元素都不相同
思路:
设ai>bi的组数为x,则有x+x-k=n,推出x=(n+k)/2
所以如果n+k是个奇数的话就直接结束了
我们现在令k=(n+k)/2,那么问题就变成了找使得ai>bi的组数恰好为k的方案数
考虑二项式反演。
我们令fi表示a>b的组数恰好为i的方案数,显然答案就是fk
令gi表示a>b的组数至少为i的方案数
那么只要求出来g数组,我们直接套一个二项式反演就可以了
可以考虑通过dp来求出g数组
我们不妨先对两个数组排一下序,并设ri表示对于ai,b数组的前i个元素里面<ai的个数
设dpi,j表示前i个数字里面至少有j对a>b的配对
转移方程也很好想,如果a数组的第i个数字不选,方案数就是dpi-1,j,如果选上它,我们要为其找一个比它小的数字,那么方案数显然就是dpi-1,j-1*(ri-(j-1))
求出来dp之后,gi=(n-i)!*dp[n][i]
然后套一下二项式反演就可以了
code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=5010;
const ll mod=1e9+9;
ll n,m;
ll p[N];
ll pp[N];
ll g[N];
ll num[N];
ll dp[N][N];
ll a[N],b[N];
ll ksm(ll x,ll y)
{
ll ans=1;
while(y)
{
if(y&1) ans=ans*x%mod;
x=x*x%mod;
y>>=1;
}
return ans;
}
ll inv(ll x)
{
return ksm(x,mod-2);
}
void init()
{
p[0]=1;
for(ll i=1;i<=5000;++i) p[i]=p[i-1]*i%mod;
pp[5000]=inv(p[5000]);
for(ll i=5000-1;i>=0;--i)
pp[i]=pp[i+1]*(i+1)%mod;
}
ll C(ll n,ll m){
if(n<m) return 0;
return p[n]*pp[m]%mod*pp[n-m]%mod;
}
void solve()
{
cin>>n>>m;
if((n+m)%2)
{
cout<<0<<endl;
return;
}
m=(n+m)/2;
for(int i=1;i<=n;++i) cin>>a[i];
for(int i=1;i<=n;++i) cin>>b[i];
sort(a+1,a+1+n);
sort(b+1,b+1+n);
for(int i=1;i<=n;++i)
{
num[i]=lower_bound(b+1,b+1+n,a[i])-b-1;
}
// for(int i=1;i<=n;++i) cout<<num[i]<<" ";
// cout<<endl;
for(int i=0;i<=n;++i) dp[i][0]=1;
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
{
dp[i][j]=(dp[i-1][j]+dp[i-1][j-1]*(num[i]-j+1)%mod)%mod;
}
}
for(int i=1;i<=n;++i) g[i]=dp[n][i]*p[n-i]%mod;
ll ans=0;
for(int i=m;i<=n;++i)
{
if((i-m)%2) ans=((ans-C(i,m)*g[i]%mod)%mod+mod)%mod;
else ans=(ans+C(i,m)*g[i]%mod)%mod;
}
cout<<ans<<endl;
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
init();
//ll t;cin>>t;while(t--)
solve();
return 0;
}