题解:P16433 [APIO 2026 中国赛区] 上升
Priestess_SLG · · 题解
upd 05/13/2026:修改了若干笔误。
O(2^nn^2)
直接状压 dp 即可。
O(n^4)
这个看着就不太好计数,因此做一个经典(此处存疑)的转化:
钦定集合
S 内所有位都为上升位,则可以看作这个集合内所有位置对答案的贡献为w_i-1 ,将所有这样的贡献求和即可得到题目中给定式子的答案。
后面的过程就比较套路了。
记
转移考虑分
1. p_i=0
枚举当前以
1.1 l>\text{last\_pos}
枚举该段中共有
转移式的每一部分分别表示:
1.2 l=\text{last\_pos}
若
2. p_i\neq 0
为了方便,后面记
同样还是枚举转移点
2.1 l>\text{last\_pos}
此时考虑枚举之前的大数中有恰好
2.2 l=\text{last\_pos}
此时区间
直接模拟上述转移过程可以做到
// #include "ascend.h"
#include <bits/stdc++.h>
using namespace std;
constexpr inline void add(long long &x, long long v, long long mod) { x += v; if (x >= mod) x -= mod; }
const int N = 510;
long long p[N], w[N], a[N], prod[N][N], f[N][N], g[N], C[N][N];
int ascend(int c, int n, int mod, vector<int> P, vector<int> W) {
#define int long long
for (int i = 1; i <= n; ++i) p[i] = P[i - 1];
for (int i = 1; i < n; ++i) w[i] = W[i - 1], a[i] = (w[i] + mod - 1) % mod;
for (int i = 0; i <= n; ++i) {
C[i][0] = 1;
for (int j = 1; j <= i; ++j) C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
}
function<int(int, int)> binom = [&](int a, int b) -> int {
if (b < 0 || a < b) return 0ll;
return C[a][b];
};
for (int i = 1; i <= n; ++i) {
prod[i][i] = a[i], prod[i][i - 1] = 1;
for (int j = i + 1; j <= n; ++j) prod[i][j] = prod[i][j - 1] * a[j] % mod;
} memset(f, 0, sizeof f), f[0][0] = 1;
int las_pos = 0, las_val = 0;
for (int i = 1; i <= n; ++i) {
memset(g, 0, sizeof g);
if (!p[i]) {
for (int l = las_pos + 1; l <= i; ++l) {
int len = i - l + 1;
for (int j = 0; j < l; ++j) if (f[l - 1][j]) for (int x = 0; x <= min(len, las_val - j); ++x)
add(g[j + x], f[l - 1][j] * prod[l][i - 1] % mod * binom(las_val - j, x) % mod * binom(l - j - 1 + len - x, len - x) % mod, mod);
}
if (las_pos >= 1) for (int j = 0; j <= las_pos; ++j) if (f[las_pos][j])
add(g[j], 1ll * f[las_pos][j] * prod[las_pos][i - 1] % mod * binom(las_pos - j + i - las_pos, i - las_pos) % mod, mod);
for (int j = 0; j <= n; ++j) f[i][j] = g[j];
} else {
for (int l = las_pos + 1; l <= i; ++l) for (int j = 0; j <= l - 1; ++j) if (f[l - 1][j]) for (int r = 0; r <= l - j - 1 && r <= p[i] - las_val - 1; ++r)
add(g[j + r + i - l + 1], f[l - 1][j] * prod[l][i - 1] % mod * binom(p[i] - las_val - 1, r) % mod * binom(p[i] - 1 - j - r, i - l) % mod, mod);
if (las_pos >= 1) if (p[i] - las_val - 1 >= i - las_pos - 1) for (int j = 0; j <= las_pos; ++j) if (f[las_pos][j]) for (int r = 0; r <= las_pos - j && r <= p[i] - las_val - 1 - (i - las_pos - 1); ++r)
add(g[j + r + i - las_pos - 1 + 1], f[las_pos][j] * prod[las_pos][i - 1] % mod * binom(p[i] - las_val - 1, i - las_pos - 1) % mod * binom(p[i] - las_val - 1 - (i - las_pos - 1), r) % mod, mod);
for (int j = 0; j <= n; ++j) f[i][j] = g[j];
las_pos = i, las_val = p[i];
}
} return f[n][las_val];
}
该代码使用 Barrett 约减等技巧后可通过该题。
// #include "ascend.h"
#include <bits/stdc++.h>
using namespace std;
constexpr inline void add(long long &x, long long v, long long mod) { x += v; if (x >= mod) x -= mod; }
struct Barrett {
unsigned int m;
unsigned long long im;
inline void init(unsigned int _m) {
m = _m;
im = (unsigned long long)(-1) / m + 1;
}
inline unsigned int mul(unsigned int a, unsigned int b) const {
unsigned long long z = 1ull * a * b;
unsigned long long x = (unsigned long long)((__uint128_t)z * im >> 64);
unsigned int v = (unsigned int)(z - x * m);
if (v >= m) v += m;
if (v >= m) v -= m;
return v;
}
} bt;
const int N = 510;
long long p[N], w[N], a[N], prod[N][N], f[N][N], g[N], C[N][N];
inline long long mul(long long x, long long y) {
return bt.mul((unsigned int)x, (unsigned int)y);
}
inline long long binom(int a, int b) {
if (b < 0 || a < b) return 0;
return C[a][b];
}
int ascend(int c, int n, int mod, vector<int> P, vector<int> W) {
#define int long long
bt.init(mod);
for (int i = 1; i <= n; ++i) p[i] = P[i - 1];
for (int i = 1; i < n; ++i) w[i] = W[i - 1], a[i] = (w[i] + mod - 1) % mod;
for (int i = 0; i <= n; ++i) {
C[i][0] = 1;
for (int j = 1; j <= i; ++j) C[i][j] = C[i - 1][j - 1] + C[i - 1][j], C[i][j] %= mod;
}
for (int i = 1; i <= n; ++i) {
prod[i][i] = a[i], prod[i][i - 1] = 1;
for (int j = i + 1; j <= n; ++j) prod[i][j] = mul(prod[i][j - 1], a[j]);
}
memset(f, 0, sizeof f), f[0][0] = 1;
int las_pos = 0, las_val = 0;
for (int i = 1; i <= n; ++i) {
memset(g, 0, sizeof g);
if (!p[i]) {
for (int l = las_pos + 1; l <= i; ++l) {
int len = i - l + 1, jlim = min(l - 1, las_val);
for (int j = 0; j <= jlim; ++j) if (f[l - 1][j]) {
int base = mul(f[l - 1][j], prod[l][i - 1]), xlim = min(len, las_val - j);
for (int x = 0; x <= xlim; ++x) {
val = mul(base, C[las_val - j][x]);
val = mul(val, C[l - j - 1 + len - x][len - x]);
add(g[j + x], val, mod);
}
}
}
if (las_pos >= 1) {
for (int j = 0; j <= las_pos; ++j) if (f[las_pos][j]) {
int val = mul(f[las_pos][j], prod[las_pos][i - 1]);
val = mul(val, C[las_pos - j + i - las_pos][i - las_pos]);
add(g[j], val, mod);
}
}
for (int j = 0; j <= n; ++j) f[i][j] = g[j];
} else {
for (int l = las_pos + 1; l <= i; ++l) {
int len = i - l + 1, need = i - l;
int jlim = min(l - 1, p[i] - 1 - need);
for (int j = 0; j <= jlim; ++j) if (f[l - 1][j]) {
int base = mul(f[l - 1][j], prod[l][i - 1]);
int rlim = min({l - j - 1, p[i] - las_val - 1, p[i] - 1 - j - need});
for (int r = 0; r <= rlim; ++r) {
int val = mul(base, C[p[i] - las_val - 1][r]);
val = mul(val, C[p[i] - 1 - j - r][i - l]);
add(g[j + r + i - l + 1], val, mod);
}
}
}
if (las_pos >= 1) {
int need = i - las_pos - 1;
if (p[i] - las_val - 1 >= need) {
for (int j = 0; j <= las_pos; ++j) if (f[las_pos][j]) {
int base = mul(f[las_pos][j], prod[las_pos][i - 1]);
base = mul(base, C[p[i] - las_val - 1][need]);
rlim = min(las_pos - j, p[i] - las_val - 1 - need);
for (int r = 0; r <= rlim; ++r) add(g[j + r + i - las_pos], mul(base, C[p[i] - las_val - 1 - need][r]), mod);
}
}
}
for (int j = 0; j <= n; ++j) f[i][j] = g[j];
las_pos = i, las_val = p[i];
}
}
return f[n][las_val];
}
O(n^3)
注意到上述做法中只有
注意到上升段中值不超过
// #include "ascend.h"
#include <bits/stdc++.h>
using namespace std;
constexpr inline void add(long long &x, long long v, long long mod) { x += v; if (x >= mod) x -= mod; }
const int N = 510;
long long p[N], w[N], a[N], prod[N][N], f[N][N], g[N], C[N][N], h[N][N];
int ascend(int c, int n, int mod, vector<int> P, vector<int> W) {
#define int long long
for (int i = 1; i <= n; ++i) p[i] = P[i - 1];
for (int i = 1; i < n; ++i) w[i] = W[i - 1], a[i] = (w[i] + mod - 1) % mod;
memset(C, 0, sizeof C);
for (int i = 0; i <= n; ++i) {
C[i][0] = C[i][i] = 1 % mod;
for (int j = 1; j < i; ++j) C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
}
function<int(int, int)> binom = [&](int a, int b) -> int {
if (b < 0 || a < b) return 0ll;
return C[a][b];
};
memset(prod, 0, sizeof prod);
for (int i = 1; i <= n; ++i) {
prod[i][i - 1] = 1, prod[i][i] = a[i];
for (int j = i + 1; j <= n; ++j) prod[i][j] = prod[i][j - 1] * a[j] % mod;
}
memset(f, 0, sizeof f), memset(h, 0, sizeof h), f[0][0] = 1;
int las_pos = 0, las_val = 0;
for (int i = 1; i <= n; ++i) {
memset(g, 0, sizeof g);
if (!p[i]) {
for (int j = 0; j <= n; ++j) h[i][j] = 0;
for (int l = las_pos + 1; l <= i; ++l) {
int x = i - l + 1;
for (int j = 0; j < l; ++j) if (f[l - 1][j])
add(h[i][j + x], f[l - 1][j] * prod[l][i - 1] % mod * binom(las_val - j, x) % mod, mod);
}
for (int k = las_pos; k < i; ++k) for (int j = 0; j <= k; ++j) if (f[k][j])
add(g[j], f[k][j] * prod[k + 1][i - 1] % mod * binom(i - j, i - k) % mod, mod);
for (int k = las_pos + 1; k <= i; ++k) for (int j = 0; j <= min(k, las_val); ++j) if (h[k][j])
add(g[j], h[k][j] * prod[k][i - 1] % mod * binom(i - j, i - k) % mod, mod);
if (las_pos >= 1) for (int j = 0; j <= las_pos; ++j) if (f[las_pos][j])
add(g[j], f[las_pos][j] * prod[las_pos][i - 1] % mod * binom(las_pos - j + i - las_pos, i - las_pos) % mod, mod);
for (int j = 0; j <= n; ++j) f[i][j] = g[j];
} else {
for (int l = las_pos + 1; l <= i; ++l) for (int j = 0; j <= l - 1; ++j) if (f[l - 1][j]) for (int r = 0; r <= l - j - 1 && r <= p[i] - las_val - 1; ++r)
add(g[j + r + i - l + 1], f[l - 1][j] * prod[l][i - 1] % mod * binom(p[i] - las_val - 1, r) % mod * binom(p[i] - 1 - j - r, i - l) % mod, mod);
if (las_pos >= 1) if (p[i] - las_val - 1 >= i - las_pos - 1) for (int j = 0; j <= las_pos; ++j) if (f[las_pos][j]) for (int r = 0; r <= las_pos - j && r <= p[i] - las_val - 1 - (i - las_pos - 1); ++r)
add(g[j + r + i - las_pos], f[las_pos][j] * prod[las_pos][i - 1] % mod * binom(p[i] - las_val - 1, i - las_pos - 1) % mod * binom(p[i] - las_val - 1 - (i - las_pos - 1), r) % mod, mod);
for (int j = 0; j <= n; ++j) f[i][j] = g[j]; las_pos = i, las_val = p[i];
}
} return f[n][las_val];
}