BZOJ 3622 I have nothing to fear
确实没什么好害怕的了
题解
二项式反演
这道题目的条件十分严苛,满足条件的数字组数必须为
显然这很难推,因此我们需要放宽条件
预备动作
首先显然要排个序,设数组分别为 ,然后处理出对与每一个
,有多少个数字
比它小
当然排序是要从小到大排序(后面解释为什么)
反演过程
要求:选择的第 个数字
比
大
设 为在
中选
个数可以得到至少(不是恰好)
组满足要求的数字
设 为选择出来的结果至少有
组满足要求的数字
设 为选择出来的结果恰好有
组满足要求的数字
显然 即为答案
① ![f[i][j]](https://juejin.cn/equation?tex=f%5Bi%5D%5Bj%5D)
对于 有
因为对于 来讲,前
个
的数一定已经把
个数中的
个数给选掉了,那么对于
可选的数有
个,同时可以继承前面的方案
上面这个就是从小到大排序的原因,必须要保证
② ![F[i]](https://juejin.cn/equation?tex=F%5Bi%5D)
表示有那么 个是正经的满足条件,剩下
个随机排列即可
③ G[i]
显然有
意思就是从 个数字当中随机调出
个,满足
的条件,而挑选的方式在排序之后恰有
种
反演一下(套个公式)
最后答案的
应该满足
,即 ![G[i]=G[\frac{n+i}{2}]](https://juejin.cn/equation?tex=G%5Bi%5D%3DG%5B%5Cfrac%7Bn%2Bi%7D%7B2%7D%5D)
解题步骤
求个
求个
求个
求个
结束
代码附上
#include <iostream>
#include <cstdio>
#include <algorithm>
#define getchar() *inp++
using namespace std;
const int mod=1e9+9;
char INP[1<<20],*inp=INP;
inline long long input()
{
long long o;char c=getchar();
while(c>57||c<48)c=getchar();
for(o=0;c>47&&c<58;c=getchar())o=(o<<1)+(o<<3)+c-48;
return o;
}
long long A[2345],B[2345],t[2345];
long long C[2345][2345],f[2345][2345],F[2345],fac[2345];
int main()
{
// freopen("In.txt","r",stdin);
// freopen("O.txt","w",stdout);
fread(INP,1,1<<20,stdin);
int n=input(),k=input();
fac[0]=1;
for(int i=1;i<=n;i++)A[i]=input();
for(int i=1;i<=n;i++)B[i]=input();
sort(A+1,A+n+1);sort(B+1,B+n+1);
for(int i=1,j=1;i<=n;i++)
{
while(A[i]>B[j]&&j<=n)j++;
t[i]=j-1;
}
for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%mod;
for(int i=0;i<=n;i++)f[i][0]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
f[i][j]=(f[i-1][j]+f[i-1][j-1]*(t[i]-j+1))%mod;
for(int i=1;i<=n;i++)F[i]=(f[n][i]*fac[n-i])%mod;
for(int i=0;i<=n;i++)
{
C[i][0]=1;
for(int j=1;j<=i;j++)C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
}
k=n+k>>1;
long long ans=0;
for(long long i=k,c=1;i<=n;i++,c*=-1)
ans=(ans+C[i][k]*F[i]*c)%mod;
printf("%lld",ans<0?ans+mod:ans);
}