【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;
}