P4859 已经没有什么好害怕的了 二项式反演

这道题给出两个数组且每个数字都不同。

要求两两配对,这样每一个配对都有一个大小关系。要求第一组大的个数比第二组大的恰好k个配对。

显然一共有\(n\)个大小关系,那么容易想到\(n-k\)必然是一个偶数才会有对应方案。

那么题目其实是要求第一组比第二组大的个数恰好为\(k+\frac{n-k}{2}=m\)

\(f_i\)表示至少有\(i\)组满足要求,\(g_i\)表示恰好有\(i\)组满足要求。

那么显然\(f_i=\sum_{j=i}^nC(j,i)g_j\)

根据二项式反演\(f(n)=\sum\limits_{i=n}^m{i\choose n}g(i)\Leftrightarrow g(n)=\sum\limits_{i=n}^m(-1)^{i-n}{i\choose n}f(i)\)

可得\(g_m=\sum_{i=m}^n(-1)^{i-m}C(i,m)f_i\)

问题的关键是\(f_i,i>=m\)的求出。

若设\(w_{i,j}\)表示第一组到了\(i\)第二组到了\(j\)所能构成的最大数量。

可以发现这与\(f_i\)相差很远。

但是若是加上第三维\(k\)表示当前已经构成了的\(k\)

则时间上不满足要求。

即为精妙的是我们可以舍弃掉第二维\(j\)\(w_{i,k}\)表示当前到了i已经匹配了k个的方案数。

这样求出的\(w_{n,i}\)再乘以\((n-i)!\)就是我们要求的\(f_i\)了。

考虑\(w_{i,k}\)的转移 如果是从大到小的扫描那么很难转移因为匹配是乱序的。

而若是从小到大的扫描由于大的数永远可以匹配第二组的后缀这样就无须记录到底第二组是谁被匹配了转移仅是\(w_{i-1,k-1}*(c_i-(k-1))\)

\(c_i\)表示第\(i\)个数所能大于第二组的个数。

这道题就做完了。

code
//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cctype>
#include<queue>
#include<deque>
#include<stack>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define INF 1000000000
#define inf 100000000000000000ll
#define ldb long double
#define pb push_back
#define put_(x) printf("%d ",x);
#define putl_(x) printf("%lld ",x);
#define get(x) x=read()
#define putl(x) printf("%lld\n",x)
#define rep(p,n,i) for(int i=p;i<=n;i+=1)
#define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
#define pii pair<int,int>
#define mk make_pair
#define gf(x) scanf("%lf",&x)
#define pf(x) ((x)*(x))
#define uint unsigned long long
#define ui unsigned
#define sq sqrt
#define x(w) t[w].x
#define r(w) t[w].r
#define id(w) t[w].id
#define R(w) s[w].r
#define yy p<<1|1
#define zz p<<1
#define sum(w) t[w].sum
#define mod 1000000009
#define sc(A) scanf("%d",&A)
#define scs(A) scanf("%s",A);
#define put(A) printf("%d\n",A)
#define min(x,y) (x>=y?y:x)
#define max(x,y) (x>=y?x:y)
#define sub(x,y) (x-y<0?x-y+mod:x-y)
using namespace std;
const int MAXN=2010;
int n,k,m,T,cnt;
int fac[MAXN],inv[MAXN],a[MAXN],b[MAXN],w[MAXN];
int f[MAXN][MAXN];
inline int ksm(int b,int p)
{
	int cnt=1;
	while(p)
	{
		if(p&1)cnt=(ll)cnt*b%mod;
		b=(ll)b*b%mod;p=p>>1;
	}
	return cnt;
}
inline int C(int n,int m){return (ll)fac[n]*inv[m]%mod*inv[n-m]%mod;}

signed main()
{
	freopen("1.in","r",stdin);
	sc(n);sc(k);
	if((n-k)&1){puts("0");return 0;}
	rep(1,n,i)sc(a[i]);
	rep(1,n,i)sc(b[i]);
	sort(a+1,a+1+n);
	sort(b+1,b+1+n);
	int p=0;
	int m=k+(n-k)/2;
	rep(1,n,i)
	{
		while(p+1<=n&&b[p+1]<a[i])++p;
		w[i]=p;
	}
	f[0][0]=1;
	rep(1,n,i)rep(0,i,j)
	{
		if(j==0)f[i][0]=1;
		else
			f[i][j]=(f[i-1][j]+(ll)f[i-1][j-1]*(w[i]-(j-1)))%mod;
	}
	fac[0]=1;
	rep(1,n,i)fac[i]=(ll)fac[i-1]*i%mod;
	inv[n]=ksm(fac[n],mod-2);
	for(int i=n-1;i>=0;--i)inv[i]=(ll)inv[i+1]*(i+1)%mod;
	int ans=0;
	rep(m,n,i)
	{
		int ww=(i-m)&1?mod-1:1;
		ans=(ans+(ll)C(i,m)*f[n][i]%mod*ww%mod*fac[n-i])%mod;
	}
	put(ans);
	return 0;
}
posted @ 2023-06-18 17:52  chdy  阅读(8)  评论(0编辑  收藏  举报