版权声明:欢迎大家转载,转载请标明出处。 https://blog.csdn.net/ylsoi/article/details/85837273
题目大意:
给定两个长度为 的序列,求有多少种匹配方式,使得 的个数恰好为 个。
思路:
据说是一道二项式反演的经典例题了。
首先如果要求正好等于
个的是不太好求的,我们可以考虑求出至少为
个的方案数。
首先先把两个序列都按照从小到大的顺序排好序,然后以序列
为对象dp。
我们设
表示前
个数里面强制确定了
个
关系的方案数,记
表示在
中有多少个数<
,于是可以得到方程
由于排了序的缘故,第
个数做出的决策一定有
种。
当然,最后
还需要乘以
。
这时我们可以得到最后的答案
与
的关系为:
根据二项式反演可得:
直接
反演即可。
当然观察到
和
的关系之后,我们可以倒推利用容斥来推出
,此时可得:
这也不失为一种很好的理解方法。
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;
using namespace std;
void File(){
freopen("bzoj3622.in","r",stdin);
freopen("bzoj3622.out","w",stdout);
}
template<typename T>void read(T &_){
_=0; T f=1; char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
_*=f;
}
const ll mod=1e9+9;
const int maxn=2000+10;
int n,m,a[maxn],b[maxn];
ll fac[maxn],ifac[maxn],c[maxn],f[maxn][maxn],g[maxn],ans;
ll qpow(ll x,ll y){
ll ret=1; x%=mod;
while(y){
if(y&1)ret=ret*x%mod;
x=x*x%mod;
y>>=1;
}
return ret;
}
void ad(ll &_,ll __){_=(_+__)%mod;}
ll C(int x,int y){
return fac[x]*ifac[y]%mod*ifac[x-y]%mod;
}
int main(){
File();
read(n),read(m);
REP(i,1,n)read(a[i]);
REP(i,1,n)read(b[i]);
if((n-m)%2)return puts("0"),0;
m=(n-m)/2;
fac[0]=1;
REP(i,1,n)fac[i]=fac[i-1]*i%mod;
ifac[n]=qpow(fac[n],mod-2);
DREP(i,n-1,0)ifac[i]=ifac[i+1]*(i+1)%mod;
sort(a+1,a+n+1);
sort(b+1,b+n+1);
int p=0;
REP(i,1,n){
while(p<n && a[p+1]<b[i])++p;
c[i]=p;
}
f[0][0]=1;
REP(i,1,n)REP(j,0,i){
ad(f[i][j],f[i-1][j]);
if(j)ad(f[i][j],f[i-1][j-1]*(c[i]-j+1)%mod);
}
REP(i,0,n)f[n][i]=f[n][i]*fac[n-i]%mod;
/*REP(i,m,n)ad(ans,((i-m)%2 ? -1 : 1)*C(i,m)*f[n][i]%mod);
printf("%lld\n",(ans+mod)%mod);*/
DREP(i,n,m){
g[i]=f[n][i];
REP(j,i+1,n)ad(g[i],-C(j,i)*g[j]%mod);
}
printf("%lld\n",(g[m]+mod)%mod);
return 0;
}