题目描述

  有一个序列\(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的更多相关文章

  1. 【专题】数位DP

    [资料] ★记忆化搜索:数位dp总结 之 从入门到模板 by wust_wenhao 论文:浅谈数位类统计问题 数位计数问题解法研究 [记忆化搜索] 数位:数字从低位到高位依次为0~len-1. 高位 ...

  2. 【BZOJ2588】Count On a Tree(主席树)

    [BZOJ2588]Count On a Tree(主席树) 题面 题目描述 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第 ...

  3. LG4719 【模板】动态dp 及 LG4751 动态dp【加强版】

    题意 题目描述 给定一棵\(n\)个点的树,点带点权. 有\(m\)次操作,每次操作给定\(x,y\),表示修改点\(x\)的权值为\(y\). 你需要在每次操作之后求出这棵树的最大权独立集的权值大小 ...

  4. 【SPOJ】Count On A Tree II(树上莫队)

    [SPOJ]Count On A Tree II(树上莫队) 题面 洛谷 Vjudge 洛谷上有翻译啦 题解 如果不在树上就是一个很裸很裸的莫队 现在在树上,就是一个很裸很裸的树上莫队啦. #incl ...

  5. 【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 ...

  6. 【优化】COUNT(1)、COUNT(*)、COUNT(常量)、COUNT(主键)、COUNT(ROWID)、COUNT(非空列)、COUNT(允许为空列)、COUNT(DISTINCT 列名)

    [优化]COUNT(1).COUNT(*).COUNT(常量).COUNT(主键).COUNT(ROWID).COUNT(非空列).COUNT(允许为空列).COUNT(DISTINCT 列名) 1. ...

  7. 洛谷P4719 【模板】"动态 DP"&动态树分治

    [模板]"动态 DP"&动态树分治 第一道动态\(DP\)的题,只会用树剖来做,全局平衡二叉树什么的就以后再学吧 所谓动态\(DP\),就是在原本的\(DP\)求解的问题上 ...

  8. LG5056 【模板】插头dp

    题意 题目背景 ural 1519 陈丹琦<基于连通性状态压缩的动态规划问题>中的例题 题目描述 给出n*m的方格,有些格子不能铺线,其它格子必须铺,形成一个闭合回路.问有多少种铺法? 输 ...

  9. 【专题】区间dp

    1.[nyoj737]石子合并 传送门:点击打开链接 描述    有N堆石子排成一排,每堆石子有一定的数量.现要将N堆石子并成为一堆.合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这 ...

随机推荐

  1. oc之证书

    https://www.cnblogs.com/MrJalen/p/6813309.html iOS推送证书生成pem文件(详细步骤)   1.pem文件概述 pem文件是服务器向苹果服务器做推送时候 ...

  2. Python云端系统开发入门 pycharm代码

    html <!DOCTYPE html><html><head> <meta charset="UTF-8"> <title& ...

  3. 【问题解决方案】Dev C++ 无法调试的问题与解决

    听翁恺老师课的时候用到一个叫DevC++的编辑器. 学到调试部分的时候,老师的没问题我的报错.我?? 试一试网上查到的方法: 工具 --> 编译选项 --> 代码生成/优化 --> ...

  4. JEECG&JWT异常捕获强化处理 | Java: Meaning of catch (final SomeException e)?

    //从header中得到token String authHeader = request.getHeader(JwtConstants.AUTHORIZATION); if (authHeader ...

  5. vue单页面模板说明文档(1)

    Introduction This boilerplate is targeted towards large, serious projects and assumes you are somewh ...

  6. freemarker数值格式化

    语法:number?string(exp) 说明:根据给定的格式化表达式将数值格式化为字符串. 参数:格式化字符串,"0"表示固定占位(至少有这个位数,不足位数用0代替),&quo ...

  7. 一次linux问题分析原因的简要记录

    1. 这边功能测试 一个linux服务器 4c 16g的内存 发现总是出现异常. dotnet run 起来的一个 程序 总是会被killed 现象为: 2. 一开始怀疑是 打开的文件描述符过多 引起 ...

  8. day 7-7 线程池与进程池

    一. 进程池与线程池 在刚开始学多进程或多线程时,我们迫不及待地基于多进程或多线程实现并发的套接字通信,然而这种实现方式的致命缺陷是:服务的开启的进程数或线程数都会随着并发的客户端数目地增多而增多,这 ...

  9. php 将数组转换网址URL参数

    $array =array ( 'id' =123, 'name' = 'dopost' );echo http_build_query( $array );//得到结果id=123name=dopo ...

  10. Python:matplotlib绘制散点图

    与线型图类似的是,散点图也是一个个点集构成的.但不同之处在于,散点图的各点之间不会按照前后关系以线条连接起来. 用plt.plot画散点图     奇怪,代码和前面的例子差不多,为什么这里显示的却是散 ...