题解:P14568 【MX-S12-T3】排列
liuchuliang666 · · 题解
妈妈我能场切 T3 了。
考虑一个排列是如何生成的,一个很经典的想法是,认为一开始有一个空序列,我们依次向里面插入
如果没有看懂,可以先去看看 P3757 和 P4099。
这么做的好处是,我们不再需要关注一个位置的具体值,反而将重心放到了排名上去,而这正是我们真正需要的。
那么我们先排除掉无解的情形,就是说有一个
那么对于
考虑 DP,设
- 对于
op_i \in \{0, 1\} 的,我们填完这个数后会多出一个位置,即f_{i, j} \leftarrow f_{i - 1, j - 1} ; - 否则我们可以将这个数填在任意一个之前留给我们的位置中,填完之后留下来的位置个数一定不会变多,且可能取到不多于填前的任意一个数,即
f_{i, j} \leftarrow \sum_{k \ge j} f_{i - 1, k} 。
初始状态分
直接做是
- 对于
op_i \in \{0, 1\} 的,将f 数组向后平移一位; - 否则对
f 数组原地做一遍后缀和。
代码如下,非常简单:
namespace LCL {
constexpr int MAXN = 5e3 + 10, MAXV = MAXN << 2;
int n, a[MAXN], f[MAXN];
bool chk() { // 2, ..., 0 3, ..., 1
bool p2 = 0, p3 = 0;
rep(i, 1, n) switch (a[i]) {
case 0:
if (p2) return 0;
break;
case 1:
if (p3) return 0;
break;
case 2:
p2 = 1;
break;
case 3:
p3 = 1;
break;
}
return 1;
}
void main() {
cin >> n;
rep(i, 1, n) cin >> a[i];
if (!chk()) cout << 0, exit(0);
if (a[1] > 1)
f[1] = 1;
else
f[2] = 1;
rep(i, 2, n) {
if (a[i] <= 1) {
per(i, n + 1, 1) f[i] = f[i - 1];
continue;
}
per(i, n + 1, 1) add(f[i], f[i + 1]);
}
per(i, n + 1, 1) add(f[i], f[i + 1]);
cout << f[1];
}
} // namespace LCL