[bzoj3622]已经没有什么好害怕的了_动态规划_容斥原理

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

题目大意

数据范围:$1\le n \le 2000$ , $0\le k\le n$。



想法

首先,不难求出药片比糖果小的组数。

紧接着,我开始的想法是

$f_{(i,j)}$表示前$i$个糖果中,满足糖果比药片大的组数是$j$的方案数。

进而发现需要将两个数组排序。

到这里一切都很正常,但是我们发现了一个问题:就是我在转移的时候,分两种情况讨论。第一种是当前糖果配对的药片比自己大,第二种是比自己小。

这样的话我需要乘上两个组合数。

但是我们仔细思考一下:如果这样转移的话,排序的意义(是的前面的区间不影响后面的区间)就失效了,我们发现这鬼东西是个有后效性的转移。

然后啊....通常我们遇到有后效性的$dp$怎么办呢?$spfa$啊~

对,我就在那里$spfa$转移后效性$dp$半天最后自闭了。

因为这个后效性根本没有办法制约。

看了$cqzhangyu$的题解恍然大悟。

哦原来还可以容斥掉。

我们修改一下上面那个状态

$f_{(i,j)}$表示前$i$个糖果中,满足糖果比药片大的组数至少为$j$,且只考虑“糖果比药片大的糖果”的摆放情况的方案数。

这样的话我们就,暴力转移一下就行了。

就还是像上面一样分类讨论,但是如果是讨论比自己大的情况就直接加。

但是统计的时候需要乘上组合数,因为需要把比药片小的糖果的情况乘一下就好了嗷。

代码

#include <bits/stdc++.h>
#define N 2010
using namespace std;
#define mod 1000000009
int a[N],b[N];
typedef long long ll;
ll f[N][N],fac[N],c[N][N];
ll ans=0;
char *p1,*p2,buf[100000];
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int rd() {int x=0,f=1; char c=nc(); while(c<48) {if(c==‘-‘) f=-1; c=nc();} while(c>47) x=(((x<<2)+x)<<1)+(c^48),c=nc(); return x*f;}
int main()
{
	int n=rd(),m=rd();
	if((n+m) % 2) puts("0"),exit(0);
	m=(n+m)/2;
	fac[0]=1; for(int i=1;i<=n;i++) fac[i]=fac[i-1] * i % mod;
	for(int i=1;i<=n;i++) a[i]=rd();
	for(int i=1;i<=n;i++) b[i]=rd();
	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-1] + c[i-1][j]) % mod;
	}
	sort(a+1,a+n+1); sort(b+1,b+n+1);
	f[0][0]=1;
	for(int i=1;i<=n;i++)
	{
		int k;
		for(k=1;k<=n && b[k] < a[i];k++);
		k--; for(int j=1;j<=i;j++) f[i][j]=(f[i-1][j] + f[i-1][j-1] * max(k-j+1,0)) % mod;
		f[i][0]=f[i-1][0];
	}
	ll tmp=1;
	for(int i=m;i<=n;i++) f[n][i]=(f[n][i]*fac[n-i])%mod,ans=(ans + tmp*f[n][i]*c[i][m]%mod + mod) % mod,tmp*=(-1);
	cout << (ans + mod) % mod << endl ;
	return 0;
}

小结:做一个后效性的$dp$,另一种办法就是采用容斥原理。

原文地址:https://www.cnblogs.com/ShuraK/p/11053792.html

时间: 2024-10-12 20:05:39

[bzoj3622]已经没有什么好害怕的了_动态规划_容斥原理的相关文章

[bzoj3622]已经没有什么好害怕的了——容斥or二项式反演+DP

题目大意: 给定两个长度为\(n\)的序列,求有多少种匹配方式,使得\(a_i<b_i\)的个数恰好为\(k\)个. 思路: 据说是一道二项式反演的经典例题了. 首先如果要求正好等于\(k\)个的是不太好求的,我们可以考虑求出至少为\(k\)个的方案数. 首先先把两个序列都按照从小到大的顺序排好序,然后以序列\(b\)为对象dp. 我们设\(f_{i,j}\)表示前\(i\)个数里面强制确定了\(j\)个\(a_i<b_i\)关系的方案数,记\(c_i\)表示在\(a\)中有多少个数<\

Bzoj3622 已经没有什么好害怕的了

这题的题面全是图,而博客园的图随时有挂的危险…… 反正能去BZOJ找原题,已经没什么好害怕的了 3622: 已经没有什么好害怕的了 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 476  Solved: 231 Description Input Output Sample Input 4 2 5 35 15 45 40 20 10 30 Sample Output 4 HINT 输入的2*n个数字保证全不相同. 还有输入应该是第二行是糖果,第三

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

Description Input Output Sample Input 4 2 5 35 15 45 40 20 10 30 Sample Output 4 HINT 输入的2*n个数字保证全不相同. 还有输入应该是第二行是糖果,第三行是药片 题解: 记得以前在学校题库中做过这道题. 先判断k是否可能实现,若能,求出需要有恰好多少对糖果比巧克力大. 现将两组数各自从小到大排序.用dp[i,j]表示给前i个糖果中的j个安排数值更低的的巧克力的方案数. 设第i个糖果比前k个巧克力数值大,则除了d

dp 容斥 bzoj3622 已经没有什么好害怕的了

https://www.lydsy.com/JudgeOnline/problem.php?id=3622 XJOI题 不过没做过.. 容斥计数啊 先排序 记\(f[i][j]\)为前\(i\)位有\(j\)位固定且\(a[j] > b[k]\)的方案数 \[f[i][j] = f[i - 1][j] + f[i - 1][j - 1] \times (last - (j - 1))\] \(last\)为最大的\(k\)使得\(a[i] > b[k]\) 有 \[f[n][i] = \sum

二项式反演及其应用

概念 二项式反演为一种反演形式,常用于通过 "指定某若干个" 求 "恰好若干个" 的问题. 注意:二项式反演虽然形式上和多步容斥极为相似,但它们并不等价,只是习惯上都称之为多步容斥. 引入 既然形式和多步容斥相似,我们就从多步容斥讲起. 我们都知道:$|A\cup B|=|A|+|B|-|A\cap B|$ ,这其实就是容斥原理. 它的一般形式为: $$|A_1\cup A_2\cup...\cup A_n|=\sum\limits_{1\le i\le n}|A_

Google&#39;s C++ coding style

v0.2 - Last updated November 8, 2013 源自 Google's C++ coding style rev. 3.274 目录 由 DocToc生成     头文件        #define用法        前向声明        内联函数        -inl.h文件        函数参数顺序        include的命名和顺序    作用域        命名空间            未命名空间            命名空间       

Google C++ 风格指南内容整理

之前一直没有全面的看过Google C++风格指南,现在很多公司进行C++开发都要求按照Google C++风格.在这个网站 http://zh-google-styleguide.readthedocs.org/en/latest/contents/  有人已经把其翻译成中文.为了便于以后查看,下面的内容完全是来自于这个网站,只是把多个网页的内容整理放在了一起. 1.      头文件: 通常每一个.cc文件都有一个对应的.h文件.也有一些常见例外,如单元测试代码和只包含main()函数的.c

Google开发规范

v0.2 - Last updated November 8, 2013 源自 Google's C++ coding style rev. 3.274 目录 由 DocToc生成     头文件        #define用法        前向声明        内联函数        -inl.h文件        函数参数顺序        include的命名和顺序    作用域        命名空间            未命名空间            命名空间       

Scala学习(七)---包和引入

包和引入 摘要: 在本篇中,你将会了解到Scala中的包和引入语句是如何工作的.相比Java不论是包还是引入都更加符合常规,也更灵活一些.本篇的要点包括: 1. 包也可以像内部类那样嵌套 2. 包路径不是绝对路径 3. 包声明链x.y.z并不自动将中间包x和x.y变成可见 4. 位于文件顶部不带花括号的包声明在整个文件范围内有效 5. 包对象可以持有函数和变量 6. 引入语句可以引入包.类和对象 7. 引入语句可以出现在任何位置 8. 引入语句可以重命名和隐藏特定成员 9. java.lang.