• BZOJ3622 已经没有什么好害怕的了 动态规划 容斥原理 组合数学


    原文链接https://www.cnblogs.com/zhouzhendong/p/9276479.html

    题目传送门 - BZOJ3622

    题意

      给定两个序列 $a,b$ ,各包含 $n$ 个数字。

      现在给 $a$ 中元素与 $b$ 中元素配对。问使得所有配对中 $a_?>b_?$ 的个数比 $a_?<b_?$ 的个数恰好多 $k$ 的方案总数。

      答案对 $10^9+9$ 取模,保证 $a$ 和 $b$ 中的所有数字互不相同。

      $nleq 2000$

    题解

      首先闭着眼睛排个序。

      然后,我们发现一个非常好的性质:如果要使得 $a_i$ 的配对 $b_?$ 满足 $a_i>b_?$ 那么,满足 $1leq ?leq c_i$ ,其中 $c_i$ 表示在 $1leq jleq n$ 中满足 $b_j<a_i$ 的最大 $j$ 值。

      我们先把问题转化一下,变成求恰好得到 $x$ 个 $a_?>b_?$ 的方案数。那么显然 $x=cfrac{n+k}{2}$ 。如果 $x$ 不是整数,那么答案显然是 $0$ 。

      下面说的满足条件是指 $a_?>b_?$ 。

      于是我们考虑动态规划。

      记 $f_{i,j}$ 表示在 $a_1,a_2,...,a_i$ 中选择 $j$ 个,匹配比他小的 $b_i$ 的方案数。

      那么我们可以轻松确定转移方程:

    $$f_{i,j}=f_{i-1,j}+f_{i-1,j-1} imes max(c_i-j+1,0)$$

      我们假设 $f_i=f_{n,i}$ ,并设 $g_i$ 为配对所有的元素,恰好有 $i$ 个满足条件的方案数。

      于是下式成立:

    $$f_i=sum_{j=i}^ninom{j}{i}g_j$$  

      回忆一下 $f$ 的定义,$f_i$ 表示在 $n$ 个里面选择 $i$ 个匹配 $i$ 个满足条件的方案数。

      考虑所有的最终情况。

      满足条件个数为 $j$ 的总共有 $g_j$ 种。每一个这样的方案中选择的 $j$ 个数中,任选 $i$ 个,所得到的方案,都会对 $f_i$ 有贡献。

      所以,$f_i=sum_{j=0}^{n}inom{j}{i}g_j=sum_{j=i}^ninom{j}{i}g_j$ 。

      于是我们来用 $f$ 表示 $g$ 。

    $$egin{eqnarray*}g_k&=&sum_{i=k}^{n}g_iinom{i}{k}sum_{j=k}^{i}(-1)^{j-k}inom{i-k}{j-k} \&=&sum_{i=k}^{n}g_isum_{j=k}^{i}(-1)^{j-k}inom{i}{k}inom{i-k}{j-k}\&=&sum_{i=k}^{n}g_isum_{j=k}^{i}(-1)^{j-k}inom{i}{j}inom{j}{k}\&=&sum_{i=k}^{n}(-1)^{i-k}inom{i}{k}sum_{j=i}^{n}g_jinom{j}{i}\&=&sum_{i=k}^{n}f_i(-1)^{i-k}inom{i}{k}end{eqnarray*}$$

      然后按照这个公式算一算就可以了。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=2005,mod=1e9+9;
    int n,k,a[N],b[N],c[N];
    int C[N][N],Fac[N];
    int f[N][N];
    int main(){
    	scanf("%d%d",&n,&k);
    	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+n+1),sort(b+1,b+n+1);
    	for (int i=1;i<=n;i++)
    		for (c[i]=c[i-1];c[i]<n&&b[c[i]+1]<a[i];c[i]++);
    	for (int i=Fac[0]=1;i<=n;i++)
    		Fac[i]=1LL*Fac[i-1]*i%mod;
    	for (int i=0;i<=n;i++)
    		C[i][i]=C[i][0]=1;
    	for (int i=1;i<=n;i++)
    		for (int j=1;j<i;j++)
    			C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
    	for (int i=0;i<=n;i++)
    		f[i][0]=1;
    	for (int i=1;i<=n;i++)
    		for (int j=1;j<=n;j++)
    			f[i][j]=(1LL*f[i-1][j-1]*max(c[i]-j+1,0)+f[i-1][j])%mod;
    	int ans=0,x=(n+k)/2;
    	if (x*2!=n+k){
    		puts("0");
    		return 0;
    	}
    	for (int i=x;i<=n;i++)
    		ans=(1LL*f[n][i]*C[i][x]%mod*Fac[n-i]*((i-x+1)%2*2-1)+ans)%mod;
    	ans=(ans+mod)%mod;
    	printf("%d",ans);
    	return 0;
    }
    

      

  • 相关阅读:
    491 · 回文数
    936 · 首字母大写
    1343 · 两字符串和
    1535 · 转换成小写字母
    13 · 字符串查找
    146 · 大小写转换 II
    241 · 转换字符串到整数(容易版)
    46 · 主元素
    kotlin协程——>通道
    kotlin协程——>异步流
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/BZOJ3622.html
Copyright © 2020-2023  润新知