1. 程式人生 > 實用技巧 >luoguP4859 已經沒有什麼好害怕的了(二項式反演)

luoguP4859 已經沒有什麼好害怕的了(二項式反演)

luoguP4859 已經沒有什麼好害怕的了(二項式反演)

祭奠天國的bzoj。

luogu

題解時間

先特判 $ n - k $ 為奇數無解。

為了方便下記 $ m = ( n + k ) / 2 $ 為 $ A>B $ 的個數。

恰好改欽定。

設 $ dp( i , j ) $ 為考慮 $ A $ 的前 $ i $ 個數欽定 $ j $ 對 $ A>B $ 的方案數。

有欽定 $ g( i ) = dp( n , i ) \times ( n - i )! $ 。

然後直接二項式反演 $ f( m ) = \sum\limits_{ i = m }^{ n } ( - 1 )^{ i - m } \binom{ i }{ m } g( i ) $ 。

程式碼

#include<bits/stdc++.h>
using namespace std;
typedef long long lint;
struct pat{int x,y;pat(int x=0,int y=0):x(x),y(y){}bool operator<(const pat &p)const{return x==p.x?y<p.y:x<p.x;}};
template<typename TP>inline void read(TP &tar)
{
	TP ret=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){ret=ret*10+(ch-'0');ch=getchar();}
	tar=ret*f;
}
namespace RKK
{
const int N=2011,mo=1000000009;
lint add(lint a,lint b){return (a+=b)>=mo?a-mo:a;}
void doadd(lint &a,lint b){if(b<0) b+=mo;if((a+=b)>=mo) a-=mo;}
lint fac[N],c[N][N];
void init()
{
	fac[0]=1;for(int i=1;i<=2000;i++) fac[i]=fac[i-1]*i%mo;
	for(int i=0;i<=2000;i++) c[i][0]=1;
	for(int i=1;i<=2000;i++)for(int j=1;j<=i;j++) c[i][j]=add(c[i-1][j-1],c[i-1][j]);
}
int n,m,a[N],b[N];
lint dp[N][N],ans;
int main()
{
	init(),read(n),read(m);for(int i=1;i<=n;i++) read(a[i]);for(int i=1;i<=n;i++) read(b[i]);sort(a+1,a+1+n),sort(b+1,b+1+n);
	if((n-m)&1){puts("0");return 0;}m=(n+m)/2;
	for(int i=0;i<=n;i++) dp[i][0]=1;
	for(int i=1,k=1;i<=n;i++)
	{
		while(k<=n&&a[i]>b[k]) k++;
		for(int j=1;j<=n;j++) dp[i][j]=add(dp[i-1][j],dp[i-1][j-1]*(k-j)%mo);
	}
	for(int i=m;i<=n;i++) doadd(ans,(((i-m)&1)?-1ll:1ll)*c[i][m]*dp[n][i]%mo*fac[n-i]%mo);
	printf("%lld\n",ans);
	return 0;
}
}
int main(){return RKK::main();}