題目
題目大意:有兩組長度爲n的數ab,組間數字互不相同。兩組數字之間兩兩配對,不重不漏,給定一個數字k,求滿足a中數字>b 比 b中數字>a 的組數多k的分組方案數
題目分析
1.問題轉化
因爲組間數字兩兩不同,所以其實就是告訴你了有多少組a比b大。
令新k=,那麼如果n+k是一個奇數答案爲0.
現在就變成了詢問的數目爲k的方案數
2.動態規劃
首先對ab數組排序
直接求正好爲k的不太好求,因爲我們考慮從前往後給a配對。
1.如果當前數字匹配了一個比它小的,那很好辦。
2.如果比他大,還要考慮被選的這個b組數對後面的影響。
第一種情況很好算,令表示前i個數字選了j個數字是比b組數字大的
那麼轉移方程:
其中表示b數組裏面有這麼多個數字比小
因爲我們不考慮第二種,也就是說除去前面那組a>b的b被匹配了,其餘的組b>a的我們沒有考慮,默認是都可以選。
3.二項式反演
那麼我們在n組數字裏面確定了j個是a>b,剩下的就隨便排列,所以是
這個代表a>b的組數至少是i個的情況。
我們令g(i)表示恰好有i組數字是a>b
那麼
根據二項式反演:
二項式反演的基本介紹和證明見:二項式反演
因此我們要求的就是g(k)
更新:
更新前沒講明白這個f的表達式怎麼來的,很多博文也沒說清楚,非常抱歉
反演之前:
這個組合數很多人可能不理解。
畫個圖輔助理解:我們看藍色點,表示恰好3個。那麼綠、黃、紅都代表至少兩個。
那麼在f函數裏面,恰好3個的一種方案:藍色點,在至少兩個裏面出現了三次。
因爲我們回到“至少”函數的推導:我們欽點一部分作爲動態規劃的,剩下的自由排列,黃綠紅分別代表欽點的方案,剩下的一個點代表自由排列出現的,那麼一個藍色就對應了三種“至少”方案。
因此,可以得到上面的表達式。
4.代碼
下面是AC代碼
#include<bits/stdc++.h>
using namespace std;
int read(){
char s;
int x=0,f=1;
s=getchar();
while(s<'0'||s>'9'){
if(s=='-')f=-1;
s=getchar();
}
while(s>='0'&&s<='9'){
x*=10;
x+=s-'0';
s=getchar();
}
return x*f;
}
const long long mod=1e9+9;
const int N=2020;
long long qpow(long long a,long long b){
if(b==0)return 1;
long long rec=qpow(a,b/2)%mod;
if(b&1)return rec*rec%mod*a%mod;
return rec*rec%mod;
}
int n,k;
int a[N],b[N];
int lst[N];//b中比ai小的數字個數
long long inv[N];//階乘的逆元
long long calc[N];//階乘
long long dp[N][N];
long long g[N];//n對裏面有至少i對a>b的方案數
void init(){
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=n;i++)b[i]=read();
sort(a+1,a+1+n);
sort(b+1,b+1+n);
for(int i=1,id=0;i<=n;i++){
while(b[id+1]<a[i]&&id<n)id++;
lst[i]=id;
}
calc[0]=1;
for(int i=1;i<=n;i++){
calc[i]=(long long)calc[i-1]*i%mod;
}
inv[n]=qpow(calc[n],mod-2);
for(int i=n-1;i>=0;i--){
inv[i]=(long long)inv[i+1]*(i+1)%mod;
}
}
int main(){
n=read(),k=read();
if((n+k)&1){
printf("0");
return 0;
}
k=(n+k)/2;
init();
dp[0][0]=1;
for(int i=1;i<=n;i++){
dp[i][0]=dp[i-1][0];
for(int j=1;j<=i;j++){
dp[i][j]=(dp[i-1][j]+dp[i-1][j-1]*max(0,lst[i]-j+1)%mod)%mod;
dp[i][j]%=mod;
}
}
for(int i=0;i<=n;i++){
g[i]=dp[n][i]*calc[n-i]%mod;
}
long long ans=0;
long long id=1;
for(int i=k;i<=n;i++){
ans+=(long long)id*calc[i]%mod *inv[k]%mod*inv[i-k]%mod*g[i]%mod;
ans=(ans%mod+mod)%mod;
id=-id;
}
ans=(ans%mod+mod)%mod;
printf("%lld\n",ans);
}