令我灵光一现的一道二项式反演紫题!
题目
精简版题干:
有两组数据A[1..n]和B[1..n],均为1到2n的整数且互不相同。
现将A与B中的数字两两配对。设一种方案中A比B大的数对有x个,B比A大的数对有y个。
问x-y=k的方案数有多少,对10^9+9取模。
思路
题目要求了“一种方案中A比B大的数对有x个,B比A大的数对有y个”,看起来需要枚举x的个数,实则不然
注意到题目还要求了x和y的差值为k,又因为两个数组的整数互不相同,所以不会出现等于的情况,所以可以得到:
解得:
那么x为定值,且当n+k不为偶数时无解
先考虑普通的dp:设表示前i个a数组值可以配对j个b数组值的方案数,表示小于的b数组值有多少,那么易得:
其中可以用双指针处理出来:
ll ii=1;
for(int i=1;i<=n;i++)
{
while(ii<=n&&b[ii]<a[i])ii++;
les[i]=ii-1;
}
数组也可以算出来,初始状态
F[0][0]=1;
for(int i=1;i<=n;i++)
{
F[i][0]=F[i-1][0];
for(int j=1;j<=i;j++)
F[i][j]=(F[i-1][j]+(les[i]-(j-1)+mod)%mod*F[i-1][j-1]%mod)%mod;
}
应用二项式反演:
表示配对a数组值大于b数组值的组数恰好为i,表示钦定(至少)配对a数组值大于b数组值的组数为i;发现g数组更好算,显然为:
朴素的理解:其中前i个“精确排”,剩下的n-i个“随便排”
那么就可以用二项式反演由g推出f(钦定与恰好的关系),再现二项式反演结论:
那么:
前文提到:,设,那么答案就是:
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=2222,M=3333,mod=1e9+9;
ll C[M][M],fac[N];
ll n,k,a[N],b[N];
ll les[N],F[N][N],I,g[N],op,fI;
//les(i):小于a(i)的b的数量
//F(i,j):前i个a恰好可以配对j个b的方案数
//F(i,j)=F(i-1,j)+[les(i)-(j-1)]*F(i-1,j-1)
//f(i):配对a大于b的组数恰好为i
//g(i):配对a大于b的组数至少为i
//g(i)=F(n,i)*(n-i)!---前i个精确排,剩下n-i个随便排
//通过二项式反演可以g→f,答案就是f(I),I=(n+k)/2
ll qpow(ll x,ll k)
{
ll res=1;
while(k)
{
if(k&1)res=res*x%mod;
x=x*x%mod;
k>>=1;
}
return res;
}
void get()
{
for(int i=0;i<M;i++)
C[i][0]=1;
for(int i=1;i<M;i++)
for(int j=1;j<=i;j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
fac[0]=fac[1]=1;
for(int i=2;i<N;i++)
fac[i]=fac[i-1]*i%mod;
}
int main()
{
get();
scanf("%lld%lld",&n,&k);
if((n+k)&1)
{
printf("0");
return 0;
}
I=(n+k)/2;
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(int i=1;i<=n;i++)
scanf("%lld",&b[i]);
sort(a+1,a+n+1);
sort(b+1,b+n+1);
ll ii=1;
for(int i=1;i<=n;i++)
{
while(ii<=n&&b[ii]<a[i])ii++;
les[i]=ii-1;
}
F[0][0]=1;
for(int i=1;i<=n;i++)
{
F[i][0]=F[i-1][0];
for(int j=1;j<=i;j++)
F[i][j]=(F[i-1][j]+(les[i]-(j-1)+mod)%mod*F[i-1][j-1]%mod)%mod;
}
for(int i=1;i<=n;i++)
g[i]=F[n][i]*fac[n-i]%mod;
for(int j=I;j<=n;j++)
{
if((j-I)&1)op=-1;
else op=1;
fI=((fI+op*C[j][I]%mod*g[j]%mod)%mod+mod)%mod;
}
printf("%lld",fI);
return 0;
}