BZOJ 3622: 已经没有什么好害怕的了
补题解系列,话说这题好久之前做的了的说
首先我们容易得出我们要求的就是糖果比药片能量大的组数(下文称此为匹配)为\(\frac{n+k}{2}\)的方案数
我们发现这个很难求,根据套路这个时候我们应该容斥,求一个至少或者至多
由于匹配\(i\)组之后剩下随便放造成的组数显然不会小于\(i\),因此我们考虑求出匹配的对数至少为\(i\)的方案数
如果我们指导所有数中选出\(i\)个匹配的方案数\(t_i\)的话 匹配的对数至少为\(i\)的方案数就是\(t_i\times(n-i)!\)
那么我们先考虑求\(t_i\),肯定还是用DP。我们先给糖果和药片都排序,那么显然我们后面枚举的糖果一定能匹配的区间一定覆盖前面的糖果
设\(f_{i,j}\)表示前\(i\)个糖果,其中匹配了\(j\)对的方案数,然后我们预处理出\(h_i\)表示第\(i\)个糖果最多能和多少个药片匹配,则显然有转移:
\[f_{i,j}=f_{i-1,j}+[h_i-(j-1)]\times f_{i-1,j-1}
\]
那么最后我们求出的\(f_{n,i}\)就是\(t_i\)了!
最后一个问题,我们怎么得出恰好为\(\frac{n+k}{2}\)对的方案数?设恰好\(i\)对为\(g_i\),显然我们可以套路地大力\(O(n^2)\)容斥:
\[g_i=f_{n,i}\times(n-i)!-\sum_{j=i+1}^{n} C_j^i\times g_j
\]
是可以通过此题的(代码没写)
然后对于这种特殊情况还有更优秀的方法——广义容斥,式子(非常优美):
\[f_n=\sum_{i=0}^n C_n^i\times g_i\Leftrightarrow g_n=\sum_{i=0}^n (-1)^{n-i}C_n^i\times f_i
\]
具体证明可以看陈指导的博客(其实我是懒得打那么多公式了233),然后:
\[g_{\frac{n+k}{2}}=\sum_{i=\frac{n+k}{2}}^n (-1)^{i-\frac{n+k}{2}}C_i^{\frac{n+k}{2}}\times f_{n,i}\times (n-i)!
\]
#include<cstdio>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=2005,mod=1e9+9;
int n,k,a[N],b[N],f[N][N],fact[N],num[N],inv[N],ans;
inline int sum(CI x,CI y)
{
int t=x+y; return t>=mod?t-mod:t;
}
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inline void init(CI n)
{
RI i; for (fact[0]=i=1;i<=n;++i) fact[i]=1LL*fact[i-1]*i%mod;
for (inv[n]=quick_pow(fact[n]),i=n-1;~i;--i) inv[i]=1LL*inv[i+1]*(i+1)%mod;
}
inline int C(CI n,CI m)
{
return 1LL*fact[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
RI i,j; for (scanf("%d%d",&n,&k),i=1;i<=n;++i) scanf("%d",&a[i]);
for (i=1;i<=n;++i) scanf("%d",&b[i]); k=n+k>>1; init(n);
for (sort(a+1,a+n+1),sort(b+1,b+n+1),i=1;i<=n;++i)
num[i]=upper_bound(b+1,b+n+1,a[i])-b-1;
for (f[0][0]=i=1;i<=n;++i) for (j=0;j<=i;++j)
f[i][j]=sum(f[i-1][j],j?(1LL*f[i-1][j-1]*(num[i]-j+1)%mod):0);
for (i=k;i<=n;++i) ans=sum(ans,1LL*((i-k)&1?mod-1:1)*f[n][i]%mod*C(i,k)%mod*fact[n-i]%mod);
return printf("%d",ans),0;
}
辣鸡老年选手AFO在即