BZOJ3622: 已经没有什么好害怕的了 DP

原创 2017年01月03日 17:12:55

题意:有N个糖果,N个药片,每个糖果和药片都有权值,求所有的两两配对方案中,糖 > 药的组数比药 >糖的组数多k组的方案数。n<=2000,保证所有权值不相同。
DP好题。
将问题转化为求糖 > 药的组数正好(n+k)/2组的方案数,若不是整数则显然为0。由于与顺序无关,所以可以先对两组数据排序。
接下来考虑如何设计状态。
排完序后,容易预处理出每一个糖果大于多少个药片(记为cnt[i])。设f[i][j] 为枚举到第i个糖果,糖 > 药有j组的方案数。转移:不使用第i个糖果,有f[i-1][j] 组;使用第i个糖果,先用前i-1个糖果配出j-1组,第i个糖果还能配对的有cnt[i]-j+1个。如果这个数是负数那么显然是不可行的。所以f[i][j] =f[i-1][j]+f[i-1][j-1]*max(cnt[i]-j+1,0)。
这样我们得到了“从n个糖果中选出k个,从n个药片中选出k个,配成k组糖 > 药的方案数“。但是由于我们没有考虑未被选出的糖和药以何种方式配对,所以这并不是糖 > 药恰好k对的方案数。我们先将得到的f[n][x] 乘以A(n-x,n-x),代表让没被选中的糖、药全排列,再考虑其中包含了什么样的重复元素。
从这里开始,已经用不到中间状态,只需考虑f[n][x](乘以全排列后的),所以将f[n][x]记为g[x]。
举个例子,比如现有一种糖 > 药正好为5组的既定方案,那么这种方案中任取三个糖 > 药的对子,都被作为”取3个糖 >药的配对“而在g[3]中出现过一次。
可以抽象理解一下:一个组数多于x的方案,就像一个高维物体,用x组的眼光去看它,看到的每一个组数为x的子集都像这个物体在x维的一个不同方面的投影。对于一个y组的方案,会产生C(y,x)个投影。那么将这些投影减去,就可以得到”正好x组“的方案数。
因此可以倒着求,求到x的时候,g[x+1]、g[x+2]…g[n]都已经是“正好”的方案数了,于是就可以得到答案了。
由于我智商有限,网上各路神犇的题解理解起来实在是绞尽脑汁,希望我的这篇文章能更加通俗生动地帮助有同样境遇的同学吧,写得不好或者不严谨请见谅。
组合数忘取模WA了3次233333

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll mob=1000000009;
ll f[2001][2001],C[2001][2001],ans[2001],suf[2001]={1};
int a[2001],b[2001],cnt[2001];
int n,k;
int main()
{
    scanf("%d%d",&n,&k);
    if((n+k)&1) return puts("0"),0;
    k=n+k>>1;
    for(int i=1;i<=n;++i) scanf("%d",a+i);sort(a+1,a+n+1);
    for(int i=1;i<=n;++i) scanf("%d",b+i);sort(b+1,b+n+1);
    for(int i=1,now=1;i<=n;++i)
    {
        while(now<=n&&b[now]<a[i]) ++now;
        cnt[i]=now-1;
    }
    f[0][0]=1;
    for(int i=1;i<=n;++i)
    {
        f[i][0]=1;
        for(int j=1;j<=n&&f[i-1][j-1];++j)
        f[i][j]=(f[i-1][j]+f[i-1][j-1]*(j<=cnt[i]?cnt[i]-j+1:0))%mob;
    }
    for(int i=0;i<=n;++i)
    {
        C[i][0]=1;
        for(int j=1;j<=i;++j)
        C[i][j]=(C[i-1][j]+C[i-1][j-1])%mob;
    }
    for(int i=1;i<=n;++i)
        suf[i]=suf[i-1]*i%mob;
    for(int i=n;i>=k;--i)
    {
        ans[i]=f[n][i]*suf[n-i]%mob;
        if(!ans[i]) continue;
        for(int j=i+1;j<=n;++j)
        {
            ans[i]-=ans[j]*C[j][i];
            if(ans[i]<0) ans[i]=ans[i]%mob+mob;
        }
    }
    printf("%lld\n",ans[k]%mob);
    return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

[BZOJ3622]已经没有什么好害怕的了(容斥原理+DP)

今天下午放假!放两天喔!~除了寒暑假和机房维修从来没放过这么长的假!

BZOJ 3622 已经没有什么好害怕的了 动态规划+容斥原理

题目大意:给定两个长度为n个序列,保证这2n个数字两两不同,求有多少匹配满足a[i]>b[i]的数对数比a[i] もう何も怖くない 题解:http://www.cnblogs.com/dyll...

BZOJ 3622 已经没有什么好害怕的了

DP+容斥原理+组合数

【BZOJ3622】已经没有什么好害怕的了 动态规划+容斥原理

题解: 首先我们给AA数组(糖果)和BB数组(药片)从小到大排个序。 lastilast_i 表示一个极大值 xx 使得 BxAiB_x 。 f(i,j)f(i,j) 表示枚举到第 AiA_i ...

BZOJ 3622 已经没有什么好害怕的了

DP+容斥然而这题还是令我很害怕。 最值问题的DP一般考虑排序之后从小到大或从大到小,一个一个地考虑。对于这题,对A,B排序,记next[i]=j,表示最大的j满足B[j] B,然后转移 f[i]...

5月20号dp专题机房模拟赛(我并没有什么可以给你,真愧怍)

题解显然显然,都tm是显然,题解都是显然,怀疑人生先说第二题吧Problem 2 (string.cpp/c/pas)【题目描述】 有一个的字符串S需要拆分成k个串,每一个串需要花费一些代价来维护。...
  • NOIAu
  • NOIAu
  • 2017-05-20 14:15
  • 215

bzoj 3622 容斥原理

题意:给出有n个元素的集合A和集合B,所有2n个元素互不相同,求将A集合中的元素和B集合中的元素两两配对,使A的元素大于B中元素的对数恰为n+K2\frac{n+K}{2} 对。设f[i][j]f[i...

java窗体Swing效果体验(并没有什么卵用)

代码(插入的图片没有显示 可能是图片大小问题) import java.awt.BorderLayout; import java.awt.Button; import java.awt.Pan...

2012年3月份工作总结 ~ 之 ~ PDF 作业对应 (虽然这个作业没有什么意思,但是非常值得总结)

(虽然这个作业没有什么意思,但是非常值得总结!!!) 前言: =============================================================...
  • sxzlc
  • sxzlc
  • 2012-03-10 00:51
  • 1537

你有10万粉丝又如何,然而并没有什么卵用!

“一民兄,我有5万粉丝了!”朋友非常兴奋地说。   我说:“你有10万粉丝又如何,然而并没用什么卵用!”   “你……”   迎面一盆冷水,对方很生气!   然而,我并非故意呛他,只是实话实说...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)