题目链接

DP

题目大意:给定一个数n,求1~n这n个整数的所有排列中有多少个波动数列,将这个数量%p后输出。

什么是波动数列呢?顾名思义,就是一个大、一个小、一个大、一个小……或者是一个小、一个大、一个小、一个大……像“5,2 ,3,1,4”和“2,3,1,5,4”这样的数列就叫做波动数列,题目里也很形象地说了。

首先,关于波动数列,我们可以推出两条性质:

性质一:在一个波动数列中,若一个数x与另一个数(x+1)不相邻,那么交换这两个数的位置就可以得出一个新的波动数列。

例子:波动数列“1,3,2,5,4”中的“1”与“2”不相邻,那么交换“1”与“2”的位置就可以得出新的波动数列“2,3,1,5,4”。

证明:对于符合这个性质的波动数列我们可以这样表示:{...... a,x,b,......,c,(x+1),d,.......}(省略号表示的是数列中的其它数),那么有四种情况:

情况一:当x为山谷,(x+1)也为山谷时,a>x,b>x,c>x+1,d>x+1。

①因为此题中的波动数列没有重复的数,所以a!=x+1,b!=x+1,所以a>x+1,b>x+1,所以当(x+1)移动到x的位置后不会破坏波动数列。

②因为c>x+1,d>x+1,所以c>x,d>x,所以当x移动到(x+1)的位置后不会破坏波动数列。

情况二:当x为山峰,(x+1)为山谷时,a<x,b<x,c>x+1,d>x+1。

①因为a<x,b<x,所以a<x+1,b<x+1,所以当(x+1)移动到x的位置后不会破坏波动数列。

②同情况一第②条

情况三:当x为山谷,(x+1)为山峰时,a>x,b>x,c<x+1,d<x+1。

①同情况一第①条。

②因为此题中的波动数列没有重复的数,所以c!=x,d!=x,所以c<x,d<x,所以当x移动到(x+1)的位置后不会破坏波动数列。

情况四:当x为山峰,(x+1)也为山峰时,a<x,b<x,c<x+1,d<x+1。

①同情况二第①条。

②同情况三第②条。

综上所述,将x与(x+1)在数列中的位置交换后可得出新的波动数列。

性质二:在一个为n的波动数列中,将每个数x都变成(n-x+1)就可以得出一个新的波动数列。

例子:波动数列“3,2,4,1”按上述步骤操作后可变成新波动数列“2,3,1,4”。

证明:这个操作其实就是把最大的数变成最小的数,最小的数变成最大的数,次大的数变成次小的数,次小的数变成次大的数……假如有这样一个山峰: a,b,c,其中a<b,c<b,那么它变形后就是这样子的:n-a+1,n-b+1,n-c+1。将变形后的三个数同时减去(n+1),即可得出-a,-b,-c,由a<b,c<b可知-a>-b,-c>-b,又因为变形后的三个数同时减去同一个数后大小关系是不变的,所以可得n-a+1>n-b+1,n-c+1>n-b+1。我们可以发现,山峰变成了山谷,但这并没有破坏波动数列,所以这是没有影响的。如果这变形之前是一个山谷也是一样的道理,大家可以自己动脑想一下。综上所属,将一个波动数列按性质二中的步骤操作后可以得出一个新的波动数列。

知道了这三条性质之后,我们就能用动态规划来解决这一问题了,状态就是f[i][j],这表示前i个数,以j为开头且j为山峰时的方案数。为什么只考虑开头为山峰时的方案数呢?因为根据性质二,将开头为山峰的、长度为n的波动数列中的每个数x都变成(n-x+1)后就是一个开头为山谷的波动数列了(证明中有写到),所以我们最后只要将开头为山峰的方案数乘2就好了。

有了状态之后,剩下的问题就是如何转移了,这里我先给出状态转移方程吧,如下:

                        f[i][j]=f[i][j-1]+f[i-1][i-j+1]

思路:对于状态转移,我们考虑从(j-1)转移过来。那么这里就有两种情况,一种是j与(j-1)不相邻,另一种是j与(j-1)相邻。

j与(j-1)不相邻的情况很好处理,因为根据性质一,若(j-1)与j不相邻,那么交换(j-1)与j的位置即可得出一个新的波动数列,所以这一种情况的方案数即为f[i][j-1]。有人可能会问,如果f[i][j-1]中包含了(j-1)与j相邻的情况怎么办?这样一交换不就错了吗?这一种情况是不可能出现的,因为f[i][j-1]是表示前i个数中以(j-1)为开头且(j-1)为山峰的方案数,那么与(j-1)相邻的只能是第二个数且第二个数一定比(j-1)要小,而j又比(j-1)要大,所以在f[i][j-1]所包含的方案中j是不会与(j-1)相邻的。

j与(j-1)相邻的情况其实也不太难想,大家请想一想,此时(j-1)紧紧地跟在j后面,这不就变成了求有i个数,(j-1)为开头且(j-1)为山谷的方案数吗?但这与我们所设立的状态表示的意义不同怎么办?没关系的,我们可以把它变一变。j后面的数都在[1,j-1]和[j+1,i]这两个区间内,一共有(i-1)个数。我们可以将[j+1,i]这个区间内的数全部减去1,就变成了[j,i-1],再与前一个区间合并一下,变成[1,i-1],这样减是不会改变数之间的相对的大小关系的,所以方案数也不会变。但此时的开头(j-1)仍是山谷啊,没错,但根据性质二,将开头为山谷的波动数列按性质中的步骤操作一番就变成开头为山峰的波动数列啦,反过来也一样。那么,我们只要求出以[(i-1)-(j-1)+1]为开头且为山峰的方案数就好了(再反一次就变回以(j-1)为开头且为山谷的方案数了嘛)。所以,这一种情况的方案数即为f[i-1][(i-1)][(i-1)-(j-1)+1],化简后就是f[i-1][i-j+1]。

综上所述,状态转移方程即为f[i][j]=f[i][j-1]+f[i-1][i-j+1]。

最后,极短的代码奉上:

#include<iostream>
#include<cstdio>
using namespace std;
int f[4205][4205];
int main()
{
int n=0,p=0,ans=0;
scanf("%d%d",&n,&p);
f[1][1]=1;//初始化
for(int i=2;i<=n;i++)
for(int j=2;j<=i;j++)//1是不可能做为山峰的,所以从2开始枚举
f[i][j]=(f[i][j-1]+f[i-1][i-j+1])%p;
for(int i=1;i<=n;i++) ans=(ans+f[n][i])%p;
printf("%d",(ans*2)%p);
return 0;
}

参考文章:https://www.luogu.org/blog/user55639/solution-p2467

Luogu P2467 [SDOI2010]地精部落 | 神奇的dp的更多相关文章

  1. luogu P2467 [SDOI2010]地精部落

    很有意思的dp计数题目. 思考一下发现开始时山峰和开始是山谷的方案数是相同的 所以我们只需要统计一个即可. 证明的话可以考虑对于任意一种开始时山峰的方案 每个数字变成n-a[i]+1 那么可以此方案还 ...

  2. 【ybt金牌导航1-2-6】【luogu P2467】地精部落

    地精部落 题目链接:ybt金牌导航1-2-6 / luogu P2467 题目大意 有一个排列,要使得每个位置要么都比两边高,要么比两边低. 而且一定要以一高一低的方式排列. 两边的只用比旁边的那个高 ...

  3. P2467 [SDOI2010]地精部落 DP

    传送门:https://www.luogu.org/problemnew/show/P2467 参考与学习:https://www.luogu.org/blog/user55639/solution- ...

  4. P2467 [SDOI2010]地精部落 (dp+组合数)【扩展Lucas好难不会】

    题目链接:传送门 题目: 题目描述 传说很久以前,大地上居住着一种神秘的生物:地精. 地精喜欢住在连绵不绝的山脉中.具体地说,一座长度为N的山脉H可分为从左到右的N段,每段有一个独一无二的高度Hi,其 ...

  5. Luogu 2467[SDOI2010]地精部落 - DP

    Solution 这题真秒啊,我眼瞎没有看到这是个排列 很显然, 有一条性质: 第一个是山峰 和 第一个是山谷的情况是一一对应的, 只需要把每个数 $x$  变成 $n-x+1$ 然后窝萌定义数组 $ ...

  6. P2467 [SDOI2010]地精部落

    题目描述 传说很久以前,大地上居住着一种神秘的生物:地精. 地精喜欢住在连绵不绝的山脉中.具体地说,一座长度为N的山脉H可分为从左到右的N段,每段有一个独一无二的高度Hi,其中Hi是1到N之间的正整数 ...

  7. 洛咕 P2467 [SDOI2010]地精部落

    同波浪,简单dp. 高度从1到n插入山脉,设f[i][j][k]表示插入了i个山脉,组成了j段,边界上有k个山脉的方案数. 那么新插入的山脉只会:插入在边界上且自己是一段.插入在边界上且与最左边的段相 ...

  8. 洛谷 P2467 [SDOI2010]地精部落

    洛谷 我讲的应该没有这个[https://www.luogu.org/blog/user55639/solution-p2467]清楚. 贴个代码算了: #include <bits/stdc+ ...

  9. Luogu 2467 [SDOI2010]地精部落

    挺有意思的题. 优质题解: https://www.luogu.org/blog/user55639/solution-p2467 题意为求长度为n,取值为$[1, n]$的波动序列的个数. 首先需要 ...

随机推荐

  1. 关于AS下Gradle安装问题总结

    在之前安装AS的随笔中简单描述了解决方法,但不够详细,在第二次创建项目时又遇到了gradle安装错误,通过在网上查找解决方法,发现方法比较多样,且描述不够仔细,本随笔将详细记录我在gradle安装中的 ...

  2. Mysql实现排序

    排序   SELECT     obj.user_id,obj.score,@rownum := @rownum + 1 AS rownum FROM     (         SELECT     ...

  3. Centos7 搭建sonarQube

       可以支持包括java,C#,C/C++,PL/SQL,Cobol,JavaScrip,Groovy等等二十几种编程语言的代码质量管理与检测: SonarQube官网:https://www.so ...

  4. 『Python』matplotlib的imshow用法

    热力图是一种数据的图形化表示,具体而言,就是将二维数组中的元素用颜色表示.热力图之所以非常有用,是因为它能够从整体视角上展示数据,更确切的说是数值型数据. 使用imshow()函数可以非常容易地制作热 ...

  5. 10分钟教你使用Picgo+GitHub+ jsDelivr搭建CDN加速免费图床

    前言 经常写Markdown或者博客的同学,肯定都要用到图床.图床是什么呢?其实相当于一个存储图片的网站,类似百度云这样,不过上传图片到图床后可以直接通过外链进行访问. 比如把本地一张a.jpg上传到 ...

  6. P7515-[省选联考 2021A卷]矩阵游戏【差分约束】

    正题 题目链接:https://www.luogu.com.cn/problem/P7515 题目大意 有一个\(n*m\)的矩形\(A\),然后给出一个\((n-1)*(m-1)\)的矩形\(B\) ...

  7. CF990G-GCD Counting【dfs】

    正题 题目链接:https://www.luogu.com.cn/problem/CF990G 题目大意 给出一棵有点权的树,对于每个\(k\)求有多条路径的点权\(gcd\)为\(k\) \(1\l ...

  8. Kubernetes-Service介绍(一)-基本概念

    前言 本篇是Kubernetes第八篇,大家一定要把环境搭建起来,看是解决不了问题的,必须实战.Pod篇暂时应该还缺少两篇,等Service和存储相关内容介绍以后,补充剩下的两篇,有状态的Pod会涉及 ...

  9. 【C++ Primer Plus】编程练习答案——第2章

    1 void ch2_1() { 2 using namespace std; 3 cout << "xxxxxxxx" << endl; 4 } 5 6 ...

  10. java 从零开始手写 RPC (04) -序列化

    序列化 java 从零开始手写 RPC (01) 基于 socket 实现 java 从零开始手写 RPC (02)-netty4 实现客户端和服务端 java 从零开始手写 RPC (03) 如何实 ...