Miraclys

一言(ヒトコト)

P3205 [HNOI2010]合唱队

P3205 [HNOI2010]合唱队

题目大意:

一个排队方式,共 \(n\) 个人( $ n\leq 1000$),如果当前人的身高大于前一个,那么将这个人排在前一个人右边,如果当前人身高小于前

一个人,那么将这个人排在前一个人左边。

现在给出最终排队顺序,求多少种初始队形方案可以得到最终结果。(答案\(\mod 19650827\)

思路:

我们发现对于一个大区间的方案数,可以由一个更小的区间推得,所以可以想到区间 \(dp\)

我们设 \(f[i][j][0]\) 表示最后一个人从左边入队得到区间 \(i \to j\) 的方案数,\(f[i][j][1]\) 表示最后一个人从右边入队得到区间 \(i \to j\) 的方案数。

如果当前一个人从左边入队:

\(a[i] < a[i + 1],\ f[i][j][0] += f[i + 1][j][0]\)

\(a[i] < a[j],\ f[i][j][0] += f[i + 1][j][1]\)

如果当前一个人从右边入队:

\(a[j] > a[j - 1],\ f[i][j][1] += f[i][j - 1][1]\)

\(a[j] > a[i],\ f[i][j][1] += f[i][j - 1][0]\)

代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 1e3 + 5;
const int Mod = 19650827;
int n, a[N], f[N][N][5];

inline int read() {
	int x = 0, f = 1;
	char c = getchar();
	while (!isdigit(c)) {
		if (c == '-') f = -1;
		c = getchar();
	}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar();
	return x * f;
}

int main() {
	n = read();
	for (int i = 1; i <= n; ++i)
		a[i] = read();
	for (int i = 1; i <= n; ++i)
		f[i][i][0] = 1;
	for (int k = 1; k <= n; ++k) {
		for (int i = 1; i <= n && i + k - 1<= n; ++i) {
			int j = i + k - 1;
			if (a[i] < a[j]) f[i][j][0] += f[i + 1][j][1];
			if (a[j] > a[j - 1]) f[i][j][1] += f[i][j - 1][1];
			if (a[i] < a[i + 1]) f[i][j][0] += f[i + 1][j][0];
			if (a[j] > a[i]) f[i][j][1] += f[i][j - 1][0];
			f[i][j][0] %= Mod;
			f[i][j][1] %= Mod;
		}
	}
	printf("%d\n", (f[1][n][1] + f[1][n][0]) % Mod);
	return 0;
} 
posted @ 2022-11-07 21:19  Miraclys  阅读(17)  评论(0编辑  收藏  举报

关于本博客样式

部分创意和图片借鉴了

BNDong

的博客,在此感谢