【BZOJ4321】queue2【dp】

【BZOJ4321】queue2【dp】

题目描述

\(n\) 个沙茶,被编号 \(1~n\)。排完队之后,每个沙茶希望,自己的相邻的两人只要无一个人的编号和自己的编号相差为 \(1\)\(+1\)\(-1\))就行;

现在想知道,存在多少方案满足沙茶们如此不苛刻的条件。

输入格式

只有一行且为用空格隔开的一个正整数 \(N\)

输出格式

一个非负整数,表示方案数对 7777777 取模。

样例

样例输入

4

样例输出

2

样例解释

有两种方案 \(2\ 4\ 1\ 3\)\(3\ 1\ 4\ 2\)

数据范围与提示

对于\(30\%\)的数据满足\(N\leqslant 20\)

对于\(100\%\)的数据满足 ;\(1\leqslant N \leqslant 10^4\)

分析

这种带有这种限制的 \(dp\) 题,我们通常的状态定义就是前 \(i\)\(j\) 结尾的方案数是多少,但是这个题并不能这样写,因为下一个放置的人的编号会影响当前放置编号后的状态,所以我们换一个思路:

定义 \(f[i][j][0/1]\) 表示向序列中插入元素 \(i\) ,当前有 \(j\) 对两两之间相差一的人, \(0/1\)表示当前插入的 \(i\) 是否和 \(i-1\)挨着,那么由于第三维的不确定性,我们需要考虑两个状态转移。

首先看 \(f[i][j][1]\) 的转移,因为 \(f[i][j][1]\) 表示当前插入 \(i\) 后有 \(j\) 对编号相差一的,因为这里表示插入后 \(i\)\(i-1\) 是相邻的,所以只可能从 \(j-1\) 对或者 \(j\) 对转移来。当 \(i-1\)\(i-2\) 相邻的时候,也就是第三维是 \(1\) ,这时候会有两种能转移来的方案:

一个是从 \(f[i-1][j][1]\) 转移来,这时候我们只需要把 \(i\)插入到 \(i-1\)\(i-2\) 中间就行。

还有一个是从 \(f[i-1][j-1][1]\) 转移来,我们只需要在 \(i-1\) 不和 \(i-2\) 挨着的那一侧插入 \(i\) 即可。

然后考虑第三维是 \(0\) ,这时候只可能从 \(f[i-1][j-1][0]\) 转移来,因为要求 \(i\)\(i-1\) 必须相邻,这时候 \(i-1\)\(i-2\) 是不相邻的,所以我们有两种插入方法,在 \(i-1\) 左侧和右侧。那么这个转移方程就是:

\[f[i][j][1] = f[i-1][j][1] + 2 \times f[i-1][j-1][0] + f[i-1][j-1][1] \]

然后就是 \(f[i][j][0]\) 的转移,当前如果 \(i\)\(i-1\) 不相邻,那么只可能让 \(j\) 不变或者减小,所以上一个的 \(j\) 只可能是 \(j\)\(j+1\)

首先考虑从 \(f[i-1][j+1][0]\) 的转移 这时候只需要在 \(j+1\) 段中任意插入个 \(i\) 就成了 \(f[i][j][0]\) ,而这时候有 \(j+1\) 种,也就是 \(f[i-1][j+1][0] \times (j+1)\)

然后是 \(f[i-1][j+1][1]\) 这时候不能插入到 \(i-1\)\(i-2\) 的这一段中,所以情况有 \(j\) 种,也就是 \(f[i-1][j+1][1] \times j\)

然后分析上一次也有 \(j\) 对的情况,如果挨着,即\(f[i-1][j][1]\),那么可以在除了这些对之外的位置放,一共有 \(i-j-2\) 种,因为也可以在 \(i-1\)\(i-2\) 之间放,所以有 \(i-j-1\) 种,不挨着的情况与这个类似,只不过只能在这些对之外的地方放,那么状态转移方程就是:

\[f[i][j][0] = f[i-1][j+1][0] \times (j+1) + f[i-1][j+1][1] \times j + f[i-1][j][1] \times (i-j-1) + f[i-1][j][0] \times (i-j-2) \]

代码

#include<cstdio>
using namespace std;
#define ll long long
const int maxn = 1000+10;
const int mod = 7777777;
ll f[maxn][maxn][2];
int main(){
	int n;
	scanf("%d",&n);
	f[2][1][1] = 2;
	for(int i=3;i<=n;++i){
		for(int j=0;j<i;++j){
			f[i][j][0] = (f[i-1][j+1][0] * (j+1) + f[i-1][j+1][1] * j + f[i-1][j][1] * (i-j-1) + f[i-1][j][0] * (i-j-2))%mod;
			if(j)f[i][j][1] = (f[i-1][j][1] + 2 * f[i-1][j-1][0] + f[i-1][j-1][1])%mod;
		}
	}
	printf("%lld\n",f[n][0][0]);
	return 0;
}
posted @ 2020-08-12 07:04  Vocanda  阅读(129)  评论(0编辑  收藏  举报