BZOJ3622 已经没有什么好害怕的了(二项式反演,组合数学)
文章目录
【注意】最后更新于 February 8, 2020,文中内容可能已过时,请谨慎使用。
题目链接
题意简述
给你两个长为 $n$ 、无重复元素的数列 $a,b$,求将 $b$ 重排后 $\left(\sum\limits_{i=1}^n[a_i>b_i]\right)-\left(\sum\limits_{i=1}^n[a_i<b_i]\right)=k$($k$ 给定)的方案数。
$n\le2000$。
简要做法
首先,$a$ 大于 $b$ 比 $b$ 大于 $a$ 多 $k$ 对可以转化为 $a$ 大于 $b$ 有 $\frac{n+k}2$ 对。
考虑 $dp$,设 $f(i,j)$ 为 $a_{1..i}$ 中选 $j$ 个数,给这 $j$ 个数每个数匹配一个小于它的 $b$ 的方案数。为方便转移,一开始要先对 $a,b$ 分别从小到大排序,这样的话若 $a_i>b_j$,必然有 $a_{i+1}>b_j$。
设比 $a_i$ 大的 $b$ 有 $cnt_i$ 个,转移方程就是 $f(i,j)=f(i-1,j)+(cnt_i-j+1)\times f(i-1,j-1)$。$cnt_i$ 可以二分查找/双指针计算。
考虑这样一个式子:$f(n,i)\times(n-i)!$,它的意义是,先从 $a$ 中选 $i$ 个数,给它们分别匹配一个小于它们的 $b$,再把剩下的 $n-i$ 个数随意匹配。这个式子并不是某种“方案数”,因为它可能会将相同的匹配方案算重。事实上,对于每种恰有 $j$ 对 $a>b$ 的匹配方案,它在 $f(n,i)\times(n-i)!$ 中被计算了 $\binom{j}{i}$ 次。令 $ans_i$ 表示恰好有 $i$ 对 $a$ 大于 $b$ 的方案数,就有 $ans_i=f(n,i)\times(n-i)!-\sum\limits_{j=i+1}^n\binom{j}{i}ans_j$。这样的话,就可以递推地计算答案。
这题还有另一种做法,叫二项式反演。我自己没有太理解清楚这种方法,所以不详细阐述。这种做法可以 $O(n)$ 地计算某个 $ans_i$,但不影响总复杂度($dp$ 还是 $O(n^2)$ 的):
$$ans_i=\sum\limits_{j=i}^n(-1)^{j-i}\binom j i f(n,j)\times(n-j)$$
参考代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=2010;
const int mod=1e9+9;
int qpow(int x,int y);
int n,k,a[N],b[N],f[N][N],ans[N],c[N][N],fac[N];
int main()
{
int i,j,cnt=0;
cin>>n>>k;
if ((n&1)!=(k&1))
{
cout<<0;
return 0;
}
k=(n+k)/2;
for (i=1;i<=n;++i) scanf("%d",a+i);
for (i=1;i<=n;++i) scanf("%d",b+i);
sort(a+1,a+n+1);
sort(b+1,b+n+1);
fac[0]=1;
for (i=1;i<=n;++i) fac[i]=(ll)fac[i-1]*i%mod;
c[0][0]=1;
for (i=1;i<=n;++i)
{
c[i][0]=1;
for (j=1;j<=i;++j) c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
f[0][0]=1;
for (i=1;i<=n;++i)
{
f[i][0]=1;
while (cnt<n&&b[cnt+1]<a[i]) ++cnt;
for (j=1;j<=cnt;++j)
{
f[i][j]=(f[i-1][j]+(ll)f[i-1][j-1]*(cnt-j+1))%mod;
}
}
for (i=n;i>=k;--i)
{
ans[i]=(ll)f[n][i]*fac[n-i]%mod;
for (j=n;j>i;--j) ans[i]=(ans[i]+mod-(ll)ans[j]*c[j][i]%mod)%mod;
}
cout<<ans[k];
return 0;
}
int qpow(int x,int y)
{
int out=1;
while (y)
{
if (y&1) out=(ll)out*x%mod;
x=(ll)x*x%mod;
y>>=1;
}
return out;
}
评论正在加载中...如果评论较长时间无法加载,你可以 搜索对应的 issue 或者 新建一个 issue 。