zoukankan      html  css  js  c++  java
  • 【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;
    }
  • 相关阅读:
    windows phone 独立存储空间的操作 (2)(转)
    Windows Phone笔记(9)使用独立存储(上)(转)
    Windows Phone笔记(8)页面间数据共享
    windows phone 使用相机并获取图片(3)(转)
    Windows Phone笔记(11)使用独立存储(下)
    Windows Phone笔记(7)页面间导航以及数据传递(转)
    Windows Phone笔记(6)使用地图服务
    Windows Phone笔记(12)XAML基础知识(转)
    Mysql基础知识
    C语言中 数组作为函数形参传递相当于指针,在函数中不能得到数组长度,只能得到指针长度4
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7787469.html
Copyright © 2011-2022 走看看