P2467 地精部落

题目链接

题意:

求可行的 波动序列 的个数(序列中数为1~n并不能重复)。

思路:

看到标签是 DP,我们就知道对于长度为 \(n\) 的可行序列的方案要由 \(n-1\) 推来,但具体怎某推呢?我们要先从简单的例子看起。

我们先看 \(4\) 的答案如何由 \(3\) 推来:

长度为三时的可行序列:

\(\qquad 2\quad 1\quad 3\)

\(\qquad 3\quad 1\quad 2\)

\(\qquad 2\quad 3\quad 1\)

\(\qquad 1\quad 3\quad 2\)

\(3\) 的可行方案得扩展到 \(4\) 的可行方案,我们可以看作向 \(3\) 的序列中插入一个极大的数,这个数比其他所有的数都要大

那我们先拿一个例子

\(\qquad 2\quad 1\quad 3\)

我们在 \(2\)\(1\) 之间插上 \(4\)

\(\qquad 2\quad 4\quad 1\quad 3\)

那么 \(2\) 就由原来的山峰变为了山谷,而插入\(4\) 一定比 \(1\) 大,所以 \(1\) 还会保持原有的山谷的形态,因此可以看作整个的序列形态不变。把 \(4\) 插到 \(1\)\(3\) 之间也一样。

而对于

\(\qquad 1\quad 3\quad 2\)

我们可以在序列最前或最后插入那个新的极大元素,而它一定比序列开头或末尾的数要大,因此 \(1\)\(2\) 还会维持原有山谷的形态,那么整个序列的形态也可看作是不变的。

因此我们推广到一般:

对于一个序列:

  • 如果它的一个边界是山峰,那我们就在其与和它相邻的那个数之间插入新元素。

  • 如果它的一个边界是山谷,那我们就在最边上插入这个元素。

这样我们就可以维持这个序列原有的形态,而一个序列有两个边界,因此我们可获得的新的且维持原有形态(即不合法元素数数量不变)的序列的个数就会是原来个数的两倍

再回到 \(3\)\(4\) 的例子,根据以上操作,我们就能由原来 \(n\)\(3\) 时的 \(4\) 个可行序列获得 \(8\) 个新的可行序列了(不合法元素的个数为 \(0\) )。

那剩下的两个可行序列从哪里来呢?

我们先把 \(n\)\(3\) 时的两个不可行序列列出来:

\(\qquad 1\quad 2\quad 3\)

\(\qquad 3\quad 2\quad 1\)

无疑,它们都是递增或递减的,中间的 \(2\) 是不合法的。
不难发现,我们只要将 \(4\) 插到 \(2\)\(1\) 之间,\(2\) 就可以变为合法的山谷,而 \(1\)\(4\) 小,因此还可维持山谷的形态,所以序列就由原来的不可行转为可行了,我们就得到剩下的那两个可行序列了。

我们再推广到一般:

对于不可行的序列(一定有一段或多段子序列是递增或递减的),我们只需将新元素插入到不合法的元素与那个比它小的元素之间,就是原不合法元素变成了山谷,而对插入位置另一边的元素无影响(因为一定还是比它大),那么原不可行序列的不合法元素的个数就一定会减少\(1\)。那如果原序列有 \(p\) 个不合法元素,那我们就有 \(p\) 个位置可以插入,这样我们就通过插入由一个含有 \(p\) 个不合法元素的序列得到了含有 \(p-1\) 个不合法元素的序列。

由以上两个一般推论我们可以发现:对于一个长度为 \(x\) 的含有\(a\)个不合法元素的序列,有两个位置可使我们为其插入新元素后其还拥有 \(a\) 个不合法元素,有 \(a\) 个位置可使我们插入新元素后使其不合法元素减 \(1\) ,并且这两种情况的位置一定是不会相重合的,那么我们就得到了长度为 \(x+1\)\(2\) 个含有 \(a\) 个不合法元素和\(a\)个含有\(a-1\)个不合法元素的序列
但我们知道对于长度为 \(x\) 的序列有 \(x+1\) 个位置是可以插入的,那应得到 \(x+1\) 个新序列,那剩下的 \(x-1-a\) 个序列呢?

可知,剩下的位置有以下几种情况:
\(z\) 代表插入位置)

  1. 位于边界山峰之外:

    \(2\quad 1\quad 3\quad z\)

    这样 \(3\) 成了不合法元素。

    即原边界山峰因比新添元素小而成不合法元素。

  2. 位于不合法元素与比它大的那个元素之间:

    \(2\quad 1\quad 3\quad 4\quad z\quad 6\quad 5\)

    这样 \(6\) 成了不合法元素。

    即那个与不合法元素相连的山峰合法元素因比新添元素小而成不合法元素。

  3. 位于合法元素之间:

    \(1\quad 5\quad z\quad 2\quad 3\)

    这样 \(5\) 成了不合法元素。

    即与新添位置相邻的山峰元素变为不合法元素。

不难发现无论是以上那种情况,都会使 不合法元素个数加 \(1\),其实把三种情况合起来,就是因为添了一个极大元素而使原山峰元素变为为不合法

因此对于一个长度为 \(x\) 的含有 \(a\) 个不合法元素的序列,我们可以通过插入得到长度为 \(x+1\)\(2\) 个含有 \(a\) 个合法元素和 \(a\) 个含有 \(a-1\) 个不合法元素和 \(x-1-a\) 个含有 \(a+1\) 个不合法元素的序列

至此我们就可以用 DP 愉快地得到所有状态了,最后输出长度为 \(n\) 不合法元素为 \(0\) 的序列的的个数就行了。

注意别忘了开 long long 并用滚动数组哦!

code:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n,p;
ll f[2][4260];
int main()
{
	cin>>n>>p;
	f[1][0]=4;
	f[1][1]=2;
	for(register int i=4;i<=n;i++)
	{
		for(int j=0;j<=i-2;j++) f[0][j]=f[1][j],f[1][j]=0; 
		for(register int j=0;j<=i-3;j++)
		{
			f[1][j]=(f[1][j]+f[0][j]*2)%p;
			if(j) f[1][j-1]=(f[1][j-1]+f[0][j]*j)%p;
			f[1][j+1]=(f[1][j+1]+f[0][j]*(i-2-j))%p;
		}
	}
	cout<<f[1][0];
 } 

代码还是很友善的对吧~

posted @ 2021-04-30 08:20  ☄️ezuyz☄️  阅读(71)  评论(0编辑  收藏  举报