【XSY2709】count DP
题目描述
有一个序列\(A\),你可以随意排列这个序列,设\(s=\sum_{i=1}^{n-1}|a_i-a_{i+1}|\) 。
问你最终\(s\leq m\)的方案数有几种。
保证\(A\)中的元素两两不同。
\(n\leq 100,a_i,m\leq 1000\)
题解
考虑从大到小把\(A\)中的元素插回去。
插入一个数时考虑的是它的相对位置(相对之前插入的其他数)。
我们把连续的一段数称作山峰,把连续的一段空位称为山谷(一个山谷中空格的个数是不确定的,因为我们只考虑相对位置)。
那么可以DP了。
设\(f_{i,j,k,S}\)为前\(i\)个数,插入完后有\(j\)个山谷,和为\(k\)的方案数。
在中间插入的转移有三种情况(设\(1\)为山峰,\(0\)为山谷):
在山峰和山谷间插入:\(01\rightarrow011\);
把一个山谷分成两个:\(101->10101\);
填上一个山谷:\(101\rightarrow 111\)
往边上插的转移类似。
这样就会带来两个问题:
和可能会加到很大在减下来。所以我们在DP到第\(i\)层时设第\(i\)层为地平面来计算排列的\(s\)值。转移时\(s\)要加上山谷的个数\(\times 2+2\)(还有两边)。
最左端和最右端的贡献很难计算。这就是状态中\(S\)的用处了。\(S\)表示确定了那些端点,如果一个端点被确定下来转移时就不会有贡献了。
时间复杂度:\(O(n^2m)\)
当然,状态可以再减小一些(也是常数级的),但我懒得改了。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<functional>
using namespace std;
typedef long long ll;
const ll p=1000000007;
ll f[2][2][110][1010];
ll g[2][2][110][1010];
int a[110];
int n,m;
void add(ll &a,ll b)
{
	a=(a+b)%p;
}
void gao(int v)
{
	memset(f,0,sizeof f);
	int j,k;
	for(j=0;j<=n/2+1;j++)
		for(k=0;k<=m;k++)
		{
			if(k+(2*j+2)*v<=m)
				add(f[0][0][j][k+(2*j+2)*v],g[0][0][j][k]);
			if(k+(2*j+1)*v<=m)
			{
				add(f[0][1][j][k+(2*j+1)*v],g[0][1][j][k]);
				add(f[1][0][j][k+(2*j+1)*v],g[1][0][j][k]);
			}
			if(k+2*j*v<=m)
				add(f[1][1][j][k+2*j*v],g[1][1][j][k]);
		}
}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("c.in","r",stdin);
	freopen("c.out","w",stdout);
#endif
	scanf("%d%d",&n,&m);
	int i,j,k;
	for(i=1;i<=n;i++)
		scanf("%d",&a[i]);
	sort(a+1,a+n+1,greater<int>());
	for(i=1;i<n;i++)
		a[i]-=a[i+1];
	g[0][0][0][0]=1;
	g[0][1][0][0]=1;
	g[1][0][0][0]=1;
	g[1][1][0][0]=1;
	gao(a[1]);
	for(i=2;i<=n;i++)
	{
		memset(g,0,sizeof g);
		for(j=0;j<=n/2+1;j++)
			for(k=0;k<=m;k++)
			{
				if(j)
				{
					//往中间放 开一个新的1
					add(g[0][0][j+1][k],j*f[0][0][j][k]);
					add(g[0][1][j+1][k],j*f[0][1][j][k]);
					add(g[1][0][j+1][k],j*f[1][0][j][k]);
					add(g[1][1][j+1][k],j*f[1][1][j][k]);
					//往中间放 和1相邻
					add(g[0][0][j][k],2*j*f[0][0][j][k]);
					add(g[0][1][j][k],2*j*f[0][1][j][k]);
					add(g[1][0][j][k],2*j*f[1][0][j][k]);
					add(g[1][1][j][k],2*j*f[1][1][j][k]);
					//往中间放 填上一个0
					add(g[0][0][j-1][k],j*f[0][0][j][k]);
					add(g[0][1][j-1][k],j*f[0][1][j][k]);
					add(g[1][0][j-1][k],j*f[1][0][j][k]);
					add(g[1][1][j-1][k],j*f[1][1][j][k]);
				}
				//往旁边放
				add(g[0][0][j+1][k],2*f[0][0][j][k]);
				add(g[0][1][j+1][k],f[0][0][j][k]);
				add(g[1][0][j+1][k],f[0][0][j][k]);
				add(g[0][0][j][k],2*f[0][0][j][k]);
				add(g[0][1][j][k],f[0][0][j][k]);
				add(g[1][0][j][k],f[0][0][j][k]);
				add(g[0][1][j+1][k],f[0][1][j][k]);
				add(g[1][1][j+1][k],f[0][1][j][k]);
				add(g[0][1][j][k],f[0][1][j][k]);
				add(g[1][1][j][k],f[0][1][j][k]);
				add(g[1][0][j+1][k],f[1][0][j][k]);
				add(g[1][1][j+1][k],f[1][0][j][k]);
				add(g[1][0][j][k],f[1][0][j][k]);
				add(g[1][1][j][k],f[1][0][j][k]);
			}
		gao(a[i]);
	}
	ll ans=0;
	for(i=0;i<=m;i++)
		add(ans,f[1][1][0][i]);
	printf("%lld\n",ans);
	return 0;
}
【XSY2709】count DP的更多相关文章
- 【专题】数位DP
		[资料] ★记忆化搜索:数位dp总结 之 从入门到模板 by wust_wenhao 论文:浅谈数位类统计问题 数位计数问题解法研究 [记忆化搜索] 数位:数字从低位到高位依次为0~len-1. 高位 ... 
- 【BZOJ2588】Count On a Tree(主席树)
		[BZOJ2588]Count On a Tree(主席树) 题面 题目描述 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第 ... 
- LG4719 【模板】动态dp 及 LG4751 动态dp【加强版】
		题意 题目描述 给定一棵\(n\)个点的树,点带点权. 有\(m\)次操作,每次操作给定\(x,y\),表示修改点\(x\)的权值为\(y\). 你需要在每次操作之后求出这棵树的最大权独立集的权值大小 ... 
- 【SPOJ】Count On A Tree II(树上莫队)
		[SPOJ]Count On A Tree II(树上莫队) 题面 洛谷 Vjudge 洛谷上有翻译啦 题解 如果不在树上就是一个很裸很裸的莫队 现在在树上,就是一个很裸很裸的树上莫队啦. #incl ... 
- 【BZOJ3956】Count 主席树+单调栈
		[BZOJ3956]Count Description Input Output Sample Input 3 2 0 2 1 2 1 1 1 3 Sample Output 0 3 HINT M,N ... 
- 【优化】COUNT(1)、COUNT(*)、COUNT(常量)、COUNT(主键)、COUNT(ROWID)、COUNT(非空列)、COUNT(允许为空列)、COUNT(DISTINCT 列名)
		[优化]COUNT(1).COUNT(*).COUNT(常量).COUNT(主键).COUNT(ROWID).COUNT(非空列).COUNT(允许为空列).COUNT(DISTINCT 列名) 1. ... 
- 洛谷P4719 【模板】"动态 DP"&动态树分治
		[模板]"动态 DP"&动态树分治 第一道动态\(DP\)的题,只会用树剖来做,全局平衡二叉树什么的就以后再学吧 所谓动态\(DP\),就是在原本的\(DP\)求解的问题上 ... 
- LG5056 【模板】插头dp
		题意 题目背景 ural 1519 陈丹琦<基于连通性状态压缩的动态规划问题>中的例题 题目描述 给出n*m的方格,有些格子不能铺线,其它格子必须铺,形成一个闭合回路.问有多少种铺法? 输 ... 
- 【专题】区间dp
		1.[nyoj737]石子合并 传送门:点击打开链接 描述 有N堆石子排成一排,每堆石子有一定的数量.现要将N堆石子并成为一堆.合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这 ... 
随机推荐
- 华为MAC Flapping , MAC的漂移
			华为写的很详细,MAC 地址的漂移会导致流量的中断. 华为阻止MAC地址漂移的方法有三种: 一.端口配置静态MAC地址 在全局视图下,执行命令mac-address static mac-addres ... 
- Centos7之Systemd(Service文件)详解
			一.开机启动 对于那些支持 Systemd 的软件,安装的时候,会自动在/usr/lib/systemd/system目录添加一个配置文件. 如果你想让该软件开机启动,就执行下面的命令(以httpd. ... 
- pycharm 报错:pycharm please specify a different SDK name
			我在给项目配虚拟环境里的解释器的时候有没有遇到过这个问题的啊,就是一个正常的项目,解释器忽然丢了,解释器是配在虚拟环境里面的,再去选择解释器就一直报这个错,给现有项目添加虚拟环境的时候也是报这个错—— ... 
- Final Destination II -- 矩阵快速幂模板题
			求f[n]=f[n-1]+f[n-2]+f[n-3] 我们知道 f[n] f[n-1] f[n-2] f[n-1] f[n-2] f[n-3] 1 1 ... 
- Python Revisited Day 01
			逻辑操作符 身份操作符 is a = ['AAA', 3, None] b = ['AAA', 3, None] a is b #False b = a a is b #True 身份比较速度快,原因 ... 
- p211有界自共轭算子T是实数集合的子集
			对条件 取非 是 ∉谱集合的实数 才对 现在是 入 属于正则点集 他 然后 又说T 的谱是实数 这不矛盾吗 这里根据 必要性 推出 蓝色和红色矛盾 矛盾就是 这是谱点 然 ... 
- 【问题解决方案】之 关于某江加密视频swf专用播放器仍无法播放的问题
			前言: 从pt上下载了一些语言学习的视频之后一直打不开,百度谷歌了若干种方法仍然无解.无奈放弃. 某日从百度知道里又看到一个方法,试了一下,居然灵了.呜呼哀哉.赶紧记下来. 原方法链接:https:/ ... 
- linux的nohup命令
			linux的nohup命令的用法. - runfox545 - 博客园https://www.cnblogs.com/allenblogs/archive/2011/05/19/2051136.htm ... 
- Android下的软件合集
			在平常使用Android手机的时候,选择一个好的软件可以做到事半功倍的效果,所以在此总结一下,加速我们的工作与生活效率 1) ConnectBot ConnectBot是一个Android操作系统上的 ... 
- js判断手机机型,然后进行相对应的操作
			我们通过浏览器内置的userAgent来判断手机机型. 具体代码如下: var u = navigator.userAgent, app = navigator.appVersion; if(/App ... 
