【洛谷P4859】已经没有什么好害怕的了

题目

题目链接:https://www.luogu.com.cn/problem/P4859
已经使 Madoka 有签订契约,和自己一起战斗的想法后,Mami 忽然感到自己不再是孤单一人了呢。
于是,之前的谨慎的战斗作风也消失了,在对 Charlotte 的傀儡使用终曲——Tiro Finale 后,Mami 面临着即将被 Charlotte 的本体吃掉的局面。
这时,已经多次面对过 Charlotte 的 Homura 告诉了学 OI 的你这样一个性质:Charlotte 的结界中有两种具有能量的元素,一种是“糖果”,另一种是“药片”,各有 \(n\) 个。在 Charlotte 发动进攻前,“糖果”和“药片”会两两配对,若恰好糖果比药片能量大的组数比“药片”比“糖果”能量大的组数多 \(k\) 组,则在这种局面下,Charlotte 的攻击会丟失,从而 Mami 仍有消灭 Charlotte 的可能。
你必须根据 Homura 告诉你的“糖果”和“药片”的能量的信息迅速告诉 Homura 这种情况的个数.
\(n,k\leq 2000\)

思路

先把 \(a,b\) 从小到大排序,记 \(\mathrm{last}_i\) 表示小于 \(a_i\)\(b_j\) 最大值的下标。
同时不难发现,\(a>b\)\(a<b\) 多恰好 \(k\) 组等价于 \(a>b\) 的为 \(\frac{n-k}{2}+k\) 组。下面令 \(k\gets \frac{n-k}{2}+k\)
我们先只考虑选择 \(a>b\) 的,不管其他如何匹配。设 \(f_{i,j}\) 表示 \(a\) 的前 \(i\) 项中,匹配了 \(j\)\(a>b\) 的情况的方案数。
因为我们不考虑除 \(a>b\) 以外的匹配,那么有

\[f_{i,j}=f_{i,j-1}+(\mathrm{last}_i-j+1)f_{i-1,j-1} \]

我们再设 \(g_i\) 表示至少有 \(i\)\(a>b\) 的匹配的方案数。那么有

\[g_i=f_i\times (n-i)! \]

因为剩余 \(n-i\) 组可以两两匹配。
但是显然 \(g\) 会计算重复,不可以直接用 \(g_k-g_{k+1}\)
我们设 \(h_i\) 表示恰好有 \(i\)\(a>b\) 的方案数。

\[g_i=\sum^{n}_{j=i}\binom{j}{i}h_j \]

根据二项式反演

\[h_j=\sum^{n}_{i=j}\binom{i}{j}(-1)^{i-j}g_i \]

直接求出 \(h_k\) 即可。时间复杂度 \(O(n^2)\)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=2010,MOD=1e9+9;
int n,m,a[N],b[N],last[N];
ll ans,f[N][N],g[N],fac[N],inv[N];

ll fpow(ll x,ll k)
{
	ll ans=1;
	for (;k;k>>=1,x=x*x%MOD)
		if (k&1) ans=ans*x%MOD;
	return ans;
}

ll C(int n,int m)
{
	return fac[n]*fpow(fac[m],MOD-2)%MOD*fpow(fac[n-m],MOD-2)%MOD;
}

int main()
{
	scanf("%d%d",&n,&m);
	if ((n-m)&1) return printf("0"),0;
	m=(n-m)/2+m;
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	for (int i=1;i<=n;i++) scanf("%d",&b[i]);
	sort(a+1,a+1+n); sort(b+1,b+1+n);
	fac[0]=f[0][0]=1;
	for (int i=1,j=1;i<=n;i++)
	{
		while (j<=n && b[j]<a[i]) j++;
		last[i]=j-1;
		fac[i]=fac[i-1]*i%MOD;
		f[i][0]=1;
	}
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
			f[i][j]=(f[i-1][j]+f[i-1][j-1]*max(0,last[i]-j+1))%MOD;
	for (int i=0;i<=n;i++)
		g[i]=f[n][i]*fac[n-i]%MOD;
	for (int i=m;i<=n;i++)
		ans=(ans+C(i,m)*fpow(-1,i-m)*g[i])%MOD;
	printf("%lld",(ans%MOD+MOD)%MOD);
	return 0;
}
posted @ 2021-01-20 10:26  stoorz  阅读(130)  评论(0编辑  收藏  举报