洛谷 P3205 [HNOI2010] 合唱队

思路

先设 \(dp[i][j]\) 为区间 \([i, j]\) 的队形方案数。

考虑如何转移:对于区间 \([i, j]\) 来说,最后一个入队的要么是 \(i\),要么是 \(j\)
所以分类讨论:

  1. \(j\) 为最后一个入队的时,\(i\)\(j - 1\) 都可能是倒数第二个入队的。要满足的条件分别是 \(h[i] < h[j]\)\(h[j - 1] < h[i]\)
  2. \(i\) 为最后一个入队的时,\(j\)\(i + 1\) 都可能是倒数第二个入队的。要满足的条件分别是 \(h[i] < h[j]\)\(h[i] < h[i + 1]\)

发现上一个状态有两种,即:在上一个状态中,最后一个进来的数(也就是当前状态的倒数第二个进来的数)是在【队尾/队头】。

因此我们要丰富一下状态定义:设 \(dp[i][j][0]\) 表示最后一个进来的数在【队头】的方案数,\(dp[i][j][1]\) 表示最后一个进来的数在【队尾】的方案数。最后答案就是 \(dp[1][n][0] + dp[1][n][1]\)

代码

#include <bits/stdc++.h>

using namespace std;

const int maxn = 1e3 + 7;
const int mod  = 19650827;

int n, a[maxn];
int dp[maxn][maxn][2];
int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i)
	    scanf("%d", a + i), 
		dp[i][i][0] = 1;

	for (int len = 2; len <= n; ++len) {
		for (int i = 1; i + len - 1 <= n; ++i) {
			int j = i + len - 1;
			
			if (a[i] < a[j]) dp[i][j][1] = (dp[i][j][1] + dp[i][j - 1][0]) % mod;
			if (a[j - 1] < a[j]) dp[i][j][1] = (dp[i][j][1] + dp[i][j - 1][1]) % mod;
		    
		    if (a[i] < a[j]) dp[i][j][0] = (dp[i][j][0] + dp[i + 1][j][1]) % mod;
			if (a[i] < a[i + 1]) dp[i][j][0] = (dp[i][j][0] + dp[i + 1][j][0]) % mod;
		}
	}
	printf("%d\n", (dp[1][n][0] + dp[1][n][1]) % mod);
	return 0;
} 
posted @ 2025-01-05 19:46  syzyc  阅读(28)  评论(0)    收藏  举报