• 【BZOJ3622】已经没有什么好害怕的了 容斥+DP


     【BZOJ3622】已经没有什么好害怕的了

    Description

    Input

    Output

    Sample Input

    4 2
    5 35 15 45
    40 20 10 30

    Sample Output

    4

    HINT

    输入的2*n个数字保证全不相同。

    还有输入应该是第二行是糖果,第三行是药片

    题解:好吧这题不是神题,而是套路题,容斥+DP的套路在很多题中都用到过,不过我虽然知道套路,却被这题的第一步卡住了。

    我们将两个序列从小到大排序。

    好吧这步看起来可能很水,正常人看到无序的序列都会先想到排序,然而为什么要两个都从小到大排呢?排序的意义何在?一会讲。

    题中给的是(糖果>药片)=(药片>糖果)+k,我们只需要用一点小学数学的知识就能算出(糖果>药片)的具体数量。下面是套路部分:我们设f[i][j]表示枚举到前i个,(糖果>药片)数至少为j的方案数。然后我们预处理出最大的k,满足药片k<糖果i,那么方程就是:

    $f[i][j]=f[i-1][j]+f[i-1][j-1]*(k-j+1)$

    现在知道排序的意义了吧?因为后面的i的决策区间一定包含前面的i的决策区间,所以只需要将其减去即可防止重复。

    那么最后答案是什么呢?套路:ans=至少有k个的方案数-至少有k+1个的+至少有k+2个的方案数-。。。

    所以答案=$sumlimits_{j=k}^nf[n][j](-1)^{j-k}C_j^k$

    P.S:这里解释一下容斥系数是$C_j^k$的原理吧,首先我们用f[i]代表上面的f[n][i],用g[i]代表恰好有i个符合条件的方案数。根据定义有:$f[i]=sumlimits_{j=i}^ng[i]C_j^i$。所以我们上面得到的最终式子可以转化成:$sumlimits_{j=k}^nf[j](-1)^{j-k}C_j^k=sumlimits_{i=k}^ng[i]sumlimits_{j=k}^i(-1)^{j-k}C_i^jC_j^k\=sumlimits_{i=k}^ng[i]sumlimits_{j=k}^i(-1)^{j-k}C_i^kC_{i-k}^{j-k}\=sumlimits_{i=k}^ng[i]C_i^ksumlimits_{j=k}^i(-1)^{j-k}C_{i-k}^{j-k}$

    根据组合恒等式后面那个东西只在i=k的时候=1,其余时候=0,所以我们就得到了g[k]。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    const ll P=1000000009;
    int n,m;
    ll ans;
    ll f[2010][2010],c[2010][2010],jc[2010];
    int a[2010],b[2010];
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')	f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+(gc^'0'),gc=getchar();
    	return ret*f;
    }
    int main()
    {
    	n=rd(),m=rd();
    	if((n^m)&1)
    	{
    		printf("0");
    		return 0;
    	}
    	m=(n+m)>>1;
    	int i,j,k;
    	for(jc[0]=i=1;i<=n;i++)	jc[i]=jc[i-1]*i%P;
    	for(i=1;i<=n;i++)	a[i]=rd();
    	for(i=1;i<=n;i++)	b[i]=rd();
    	for(i=0;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])%P;
    	}
    	sort(a+1,a+n+1),sort(b+1,b+n+1);
    	f[0][0]=1;
    	for(i=1;i<=n;i++)
    	{
    		for(k=1;k<=n&&b[k]<a[i];k++);
    		k--;
    		for(j=1;j<=i;j++)	f[i][j]=(f[i-1][j]+f[i-1][j-1]*max(k-j+1,0))%P;
    		f[i][0]=f[i-1][0];
    	}
    	ll tmp=1;
    	for(i=m;i<=n;i++)	f[n][i]=(f[n][i]*jc[n-i])%P,ans=(ans+tmp*f[n][i]*c[i][m]%P+P)%P,tmp=-tmp;
    	printf("%lld",(ans+P)%P);
    	return 0;
    }
  • 相关阅读:
    48. Rotate Image
    47. Permutations II
    46. Permutations
    45. Jump Game II
    44. Wildcard Matching
    43. Multiply Strings
    42. Trapping Rain Water
    41. First Missing Positive
    40. Combination Sum II
    39. Combination Sum
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7787469.html
Copyright © 2020-2023  润新知