【Luogu P3205】[HNOI2010]合唱队

链接:

洛谷

题目大意:

\(n\) 个人,每个人有一个高度 \(h_i\),从左到右按以下原则依次将每个人插入:

  • 第一个人直接插入空的当前队形中。
  • 对从第二个人开始的每个人,如果他比前面那个人高(\(h\) 较大),那么将他插入当前队形的最右边。如果他比前面那个人矮(\(h\) 较小),那么将他插入当前队形的最左边。

求最后排成一个给定顺序的初始队列数。

正文:

像这种在左边右边插入的,可以考虑倒着处理,设 \(f_{i,j,0/1}\) 表示当前处理到区间 \([i,j]\),且最后插入的数是插在了左边(或右边)。可以得到:

\[\begin{aligned}f_{i,j,0}\leftarrow \left\{\begin{matrix}f_{i+1,j,0}&(h_i<h_{i+1})\\ f_{i+1,j,1}&(h_i<h_j) \end{matrix}\right.\\ f_{i,j,1}\leftarrow \left\{\begin{matrix}f_{i,j-1,0}&(h_j>h_{i})\\ f_{i,j-1,1}&(h_j>h_{j-1}) \end{matrix}\right.\\ \end{aligned}\]

初值只用在 \(f_{i,i,0/1}\) 中任选一个变为 \(1\) 就好了。

代码:

const int N = 1010;
const int mod = 19650827;

inline ll Read()
{
	ll x = 0, f = 1;
	char c = getchar();
	while (c != '-' && (c < '0' || c > '9')) c = getchar();
	if (c == '-') f = -f, c = getchar();
	while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
	return x * f;
}

int n;
int a[N];
int f[N][N][2];

int main()
{
//	freopen(".in", "r", stdin);
//	freopen(".out", "w", stdout);
	n = Read();
	for (int i = 1; i <= n; i++) a[i] = Read(), f[i][i][0] = 1;
	for (int i = n; i; i--)
		for (int j = i + 1; j <= n; j++)
		{
			if (a[i] < a[i + 1]) (f[i][j][0] += f[i + 1][j][0]) %= mod;
			if (a[i] < a[j]) (f[i][j][0] += f[i + 1][j][1]) %= mod;
			if (a[j] > a[i]) (f[i][j][1] += f[i][j - 1][0]) %= mod;
			if (a[j] > a[j - 1]) (f[i][j][1] += f[i][j - 1][1]) %= mod;
		}
	printf ("%d\n", (f[1][n][0] + f[1][n][1]) % mod);
	return 0;
}

posted @ 2021-10-20 15:41  Jayun  阅读(20)  评论(0编辑  收藏  举报