BZOJ 3622 已经没有什么好害怕的了

365 阅读2分钟

BZOJ 3622 I have nothing to fear

确实没什么好害怕的了

题解

二项式反演

这道题目的条件十分严苛,满足条件的数字组数必须为 k 显然这很难推,因此我们需要放宽条件

预备动作

首先显然要排个序,设数组分别为 A、B,然后处理出对与每一个 A[i],有多少个数字 \in B 比它小

当然排序是要从小到大排序(后面解释为什么)

反演过程

要求:选择的第 i 个数字 A[i]B[i]

f[i][j] 为在 A[1-i] 中选 j 个数可以得到至少(不是恰好) j 组满足要求的数字

F[i] 为选择出来的结果至少有 i 组满足要求的数字

G[i] 为选择出来的结果恰好有 i 组满足要求的数字

显然 G[k] 即为答案

f[i][j]

对于 f[i][j]

f[i][j]=f[i-1][j]+f[i-1][j-1]*(t[i]-(j-1))

因为对于 A[i] 来讲,前 [i-1]\in A 的数一定已经把 t[i] 个数中的 i-1 个数给选掉了,那么对于 A[i] 可选的数有 t[i]-(j-1) 个,同时可以继承前面的方案 f[i-1][j]

上面这个就是从小到大排序的原因,必须要保证 t[i] \ge t[i-1]

F[i]

F[i]=f[n][i]*(n-i)!

表示有那么 i 个是正经的满足条件,剩下 n-i 个随机排列即可

③ G[i]

显然有

F[k]=\sum_{i=k}^{n} C_i^kG[i]

意思就是从 i 个数字当中随机调出 k 个,满足 F[k] 的条件,而挑选的方式在排序之后恰有 C_i^k

反演一下(套个公式)

G[k]=\sum_{i=k}^{n}(-1)^{i-k}C_i^kF[i]

最后答案的 i 应该满足 i-(n-i)>=k ,即 G[i]=G[\frac{n+i}{2}]

解题步骤

O(n) 求个 i!

O(n^2) 求个 f[n][i]

O(n^2) 求个 F[i]

O(n) 求个 G[k]

结束

代码附上

#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);
}