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;
}
标签:合唱队,int,HNOI2010,入队,P3205,getchar,Mod
From: https://www.cnblogs.com/Miraclys/p/16867520.html