2025-7-26 总结
字符串
A - Word Cut
1、算法思想维度
问题类型:\(\text{dp}\)
做题思路:可以很容易发现如果 \(s\) 可以变成 \(t\),则 \(s\) 可以通过 \(0\) 次或 \(1\) 次直接变成 \(t\)。所以可以直接枚举交换那些位置可以把 \(s\) 变为 \(t\) 然后再 \(\text{dp}\) 求解方案数即可。
2、实现细节维度
为了方便计算,建议先把 \(s\) 复制一遍,但不能写成 s[i+n]=s[i],可以写成 s+=s 或 s.push_back(s[i])。
核心代码
int C(int l, int r) {
for (int i = l, j = 0; i <= r; i++) {
if (s[i] != t[++j]) {
return 0;
}
}
return 1;
}
for (int i = 1; i <= n; i++) {
cnt += C(i, i + n - 1);
}
f[0][!C(1, n)] = 1;
for (int i = 1; i <= k; i++) {
f[i][0] = (cnt * f[i - 1][1] + (cnt - 1) * f[i - 1][0]) % kMod;
f[i][1] = ((n - cnt) * f[i - 1][0] + (n - cnt - 1) * f[i - 1][1]) % kMod;
}
3、数学建模维度
复杂度计算:
- 预处理:\(O(n^2)\)
- \(\text{dp}\):\(O(k)\)
- 总复杂度:\(O(k)\)
B - Censoring S
1、算法思想维度
问题类型:\(\text{KMP}\),栈
做题思路:看到要匹配字符串,就可以想到用 \(\text{KMP}\)。在 \(\text{KMP}\) 的过程中每次把遍历到的字符放入栈内,当匹配到时直接弹出栈,继续按上述方法做 \(\text{KMP}\)。最后把弹出栈的字符反转以下,输出即可。
2、实现细节维度
核心代码
for (int i = 2, j = 0; i <= lb; i++) {
for (; j && b[i] != b[j + 1]; j = p[j]);
j += (b[i] == b[j + 1] ? 1 : 0), p[i] = j;
}
for (int i = 1, j = 0; i <= la; i++) {
for (; j && a[i] != b[j + 1]; j = p[j]);
j += (a[i] == b[j + 1] ? 1 : 0), f[i] = j, St[++top] = i;
if (j == lb) {
top -= lb, j = f[St[top]];
}
}
3、数学建模维度
复杂度计算:
- 栈:\(O(n)\)
- \(\text{KMP}\) 匹配:\(O(n)\)
- 总复杂度:\(O(n)\)
C - Prefix Function Queries
1、算法思想维度
问题类型:\(\text{KMP}\)
做题思路:先预处理出原串的 \(\text{KMP}\)。每次处理询问串时不用重新跑原串的 \(\text{KMP}\),直接扫一遍询问串,用 \(\text{KMP}\) 匹配即可,同时要判断一下如果当前指针在 \(n\) 以内的话用预处理好的自动机 \(O(1)\) 转移。复杂度是单次 \(O(|t|)\) 的。
2、实现细节维度
每次计算完询问串后要删掉询问串的 \(\text{KMP}\) 也就是还原到原串。
核心代码
for (int i = 1; i < n; i++) {
int k = p[i - 1];
for (; k > 0 && s[i] != s[k]; k = p[k - 1]);
k += s[i] == s[k], p[i] = k;
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < 26; j++) {
cnt[i][j] = i > 0 && j != s[i] - 'a' ? cnt[p[i - 1]][j] : i + (j == s[i] - 'a');
}
}
for (cin >> q; q--;) {
cin >> t, m = t.size(), s += t;
for (int i = n; i < n + m; i++) {
for (int j = 0; j < 26; j++) {
cnt[i][j] = j != s[i] - 'a' ? cnt[p[i - 1]][j] : i + (j == s[i] - 'a');
}
p.push_back(cnt[p[i - 1]][s[i] - 'a']);
cout << p[i] << ' ';
}
cout << '\n';
for (int i = 0; i < m; i++) {
p.pop_back(), s.pop_back();
}
}
3、数学建模维度
复杂度计算:
- \(\text{KMP}\)(原串):\(O(n)\)
- 预处理:\(O(26n)\)
- \(\text{KMP}\)(询问串):\(O(10)\)
- 总复杂度:\(O(26n)\)
D - Restoring the Expression
1、算法思想维度
问题类型:嘻哈
做题思路:由于需要实现字符串相加的运算,所以可以换一个乘数,把 \(Base\) 设为 \(10\)。但是这样一来,冲突的概率就大了很多,所以要双哈希,并且还要换一些模数。对于哈希可以枚举等号的位置,假设等号后面的数长为 \(len\),由于加法最多进一位,所以加号前的数长度为 \(len\) 或 \(len−1\),加号后面的数同理。
2、实现细节维度
要判断是否有前导零。
核心代码
int H1(int l, int r) { return (f[r] - f[l - 1] * fx[r - l + 1] % kMod1 + kMod1) % kMod1; }
int H2(int l, int r) { return (g[r] - g[l - 1] * gx[r - l + 1] % kMod2 + kMod2) % kMod2; }
int C(int l, int r) {
if (s[l] != '0') {
return 1;
}
for (int i = l + 1; i <= r; i++) {
if (s[i] == '0') {
return 0;
}
}
return 1;
}
int C1(int len, int n, int flag) { return n - (len << 1) + flag < 1 ? 0 : ((H1(1, n - (len << 1) + flag) + H1(n - (len << 1) + flag + 1, n - len)) % kMod1) == H1(n - len + 1, n) && C(n - (len << 1) + flag + 1, n - len); }
int C2(int len, int n, int flag) { return n - (len << 1) + flag < 1 ? 0 : ((H2(1, n - (len << 1) + flag) + H2(n - (len << 1) + flag + 1, n - len)) % kMod2) == H2(n - len + 1, n) && C(n - (len << 1) + flag + 1, n - len); }
int C3(int len, int n, int flag) { return len + flag < 2 ? 0 : ((H1(1, len + flag - 1) + H1(len + flag, n - len)) % kMod1) == H1(n - len + 1, n) && C(len + flag, n - len); }
int C4(int len, int n, int flag) { return len + flag < 2 ? 0 : ((H2(1, len + flag - 1) + H2(len + flag, n - len)) % kMod2) == H2(n - len + 1, n) && C(len + flag, n - len); }
void U1(int len, int flag) { (n - (len << 1) + flag < pos1) && (pos1 = n - (len << 1) + flag, pos2 = n - len); }
void U2(int len, int flag) { (len + flag - 1 < pos1) && (pos1 = len + flag - 1, pos2 = n - len); }
for (int i = 1; i <= n; i++) {
fx[i] = fx[i - 1] * 10 % kMod1, f[i] = (f[i - 1] * 10 + s[i] - '0') % kMod1;
gx[i] = gx[i - 1] * 10 % kMod2, g[i] = (g[i - 1] * 10 + s[i] - '0') % kMod2;
}
for (int i = n / 3; i <= (n >> 1); i++) {
if (C(n - i + 1, n)) {
(C1(i, n, 0) && C2(i, n, 0)) && (U1(i, 0), 0);
(C1(i, n, 1) && C2(i, n, 1)) && (U1(i, 1), 0);
(C3(i, n, 0) && C4(i, n, 0)) && (U2(i, 0), 0);
(C3(i, n, 1) && C4(i, n, 1)) && (U2(i, 1), 0);
}
}
3、数学建模维度
复杂度计算:
- 预处理:\(O(n)\)
- 哈希:\(O(n\log n)\)
- 总复杂度:\(O(n\log n)\)
E - Gene Folding
1、算法思想维度
问题类型:\(\text{Manacher}\)
做题思路:先做一边 \(\text{Manacher}\),然后 \(l\) 从前往后扫一遍,如果遇到一个字母不可删除,则记录下这个字符的位置。\(r\) 指针同理,从后往前扫找到第一个不可删除的位置。最后的答案也就是 \(\frac{r-l}{2}\)(因为前面在每两个字母中加上其它符号,所以最后要除以 \(2\))。
2、实现细节维度
因为要翻折,所以回文的长度一定要为偶数,所以中间的位置一定为后面添加的符号。
核心代码
cin >> a, n = strlen(a), b[cnt++] = '&', b[cnt] = '#';
for (int i = 0; i < n; i++) {
b[++cnt] = a[i], b[++cnt] = '#';
}
for (int i = 1, j = 0, mid = 0; i <= cnt; i++) {
(i < j) && (p[i] = min(p[(mid << 1) - i], j - i + 1));
for (; b[i - p[i]] == b[i + p[i]]; p[i]++);
(p[i] + i > j) && (j = p[i] + i - 1, mid = i);
}
int l = 1, r = cnt;
for (int i = 1; i <= cnt; i++) {
(b[i] == '#' && i - p[i] + 1 <= l) && (l = i);
}
for (int i = cnt; i > l; i--) {
(b[i] == '#' && i + p[i] - 1 >= r) && (r = i);
}
3、数学建模维度
复杂度计算:
- \(\text{Manacher}\):\(O(n)\)
- 查询位置:\(O(n)\)
- 总复杂度:\(O(n)\)
F - Matching
1、算法思想维度
问题类型:\(\text{KMP}\)
做题思路:这道题很容易就能想到如果 \(c_i\) 在这一段范围内排在与 \(p_i\) 同样的位置就能算作匹配。于是预处理 \(p\) 排列中 \(p_i\) 的前驱和后继的位置。匹配时,判断 \(c_i\) 是否大于(或小于)他所要匹配的 \(p_i\) 的前驱(或后继)即可。
2、实现细节维度
核心代码
for (int i = 1; i <= n; i++) {
cin >> b[i], a[b[i]] = i, pre[i] = i - 1, net[i] = i + 1;
}
for (int i = n; i >= 1; i--) {
(pre[a[i]]) && (L[i] = b[pre[a[i]]] - i), (net[a[i]] <= n) && (R[i] = b[net[a[i]]] - i);
pre[net[a[i]]] = pre[a[i]], net[pre[a[i]]] = net[a[i]];
}
for (int i = 2, j = 0; i <= n; i++) {
for (; j && !(a[i + L[j + 1]] <= a[i] && a[i + R[j + 1]] >= a[i]); j = p[j]);
j += a[i + L[j + 1]] <= a[i] && a[i + R[j + 1]] >= a[i], p[i] = j;
}
for (int i = 1; i <= m; i++) {
cin >> c[i];
}
for (int i = 1, j = 0; i <= m; i++) {
for (; j && !(c[i + L[j + 1]] <= c[i] && c[i + R[j + 1]] >= c[i]); j = p[j]);
j += c[i + L[j + 1]] <= c[i] && c[i + R[j + 1]] >= c[i], (j == n) && (ans[++k] = i - n + 1, j = p[j]);
}
3、数学建模维度
复杂度计算:
- 预处理:\(O(n)\)
- 统计答案:\(O(n)\)
- 计算答案:\(O(n)\)
- 总复杂度:\(O(n)\)
H - Forbidden Indices
1、算法思想维度
问题类型:后缀数组,并查集,栈
做题思路:可以发现同一个子串的出现在 \(\text{SA}\) 数组中一定是连续的。所以得到的 \(\text{LCP}\) 数组 \(h\),如果 \(h_i\ge l\) 就连接 \(i\) 和 \(i-1\)。那么每个连通块就是一个长度为 \(l\) 的子串的最大出现次数,取 \(\max\) 即可得到答案。但由于复杂度高,所以可以从大到小枚举子串大小,然后用并查集维护连边即可 \(\text{AC}\)。
2、实现细节维度
字符串 \(s\) 和 \(t\) 为了方便计算,要先 reverse 一下。
核心代码
int F(int x) { return fa[x] == x ? x : fa[x] = F(fa[x]); };
void M(int x, int y) { x = F(x), y = F(y), (x != y) && (fa[x] = y, sz[y] += sz[x], sz[x] = 0); }
for (int i = 1; i <= n; i++) {
sa[i] = i, rk[i] = s[i];
}
for (int w = 1; w <= n; w <<= 1) {
sort(sa + 1, sa + 1 + n, [w](int a, int b) { return (rk[a] == rk[b] ? rk[a + w] < rk[b + w] : rk[a] < rk[b]); });
for (int i = 1, r = 0; i <= n; i++) {
rk_[sa[i]] = rk[sa[i]] == rk[sa[i - 1]] && rk[sa[i] + w] == rk[sa[i - 1] + w] ? r : ++r;
}
copy(rk_, rk_ + kMaxN, rk);
}
for (int i = 1, cur = 0; i <= n; i++) {
if (rk[i] != 1) {
for (cur -= !!cur; s[i + cur] == s[sa[rk[i] - 1] + cur]; cur++);
h[rk[i]] = cur;
}
}
for (int i = 1; i <= n; i++) {
if (t[i] == '0') {
ans = n - i + 1;
break;
}
}
for (int i = 1; i <= n; i++) {
fa[i] = i, sz[i] = (t[sa[i]] != '1'), vec[h[i]].push_back(i);
}
for (int i = n; i >= 0; i--) {
for (int x : vec[i]) {
(x > 1) && (M(x, x - 1), 0), ans = max(ans, i * sz[F(x)]);
}
}
3、数学建模维度
复杂度计算:
- 后缀数组:\(O(n\log^2n)\)
- 栈:\(O(n)\)
- 并查集:\(O(n\log n)\)
- 总复杂度:\(O(n\log^2n)\)
I - You Are Given Some Strings...
1、算法思想维度
问题类型:\(\text{ACAM}\)
做题思路:对每个位置 \(p\) 记录它的前缀的所有后缀能与多少 \(s_i\) 相等。如果再加上 \(s_j\) ,就要求一个位置的后缀有多少与 \(s_j\) 相等的前缀,即为 \(b_p\)。那么可以建 \(s\) 所有反串的 \(\text{ACAM}\),再用 \(t\) 的反串上去跑即可。根据乘法原理,答案为 \(\sum_{i=1}^na_ib_{i+1}\)。
2、实现细节维度
要分清楚 \(\text{ACAM}\) 和反串 \(\text{ACAM}\),不要打错了,不然会很难调试。
核心代码
void I(string s, int id) {
int p = 0;
for (char it : s) {
(!son[id][p][it - 'a']) && (son[id][p][it - 'a'] = ++cnt[id]), p = son[id][p][it - 'a'];
}
ed[id][p]++;
}
void B(int id) {
queue<int> q;
for (int i = 0; i < 26; i++) {
(son[id][0][i]) && (q.push(son[id][0][i]), 0);
}
for (; !q.empty(); q.pop()) {
int t = q.front();
for (int i = 0; i < 26; i++) {
(son[id][t][i]) ? (f[id][son[id][t][i]] = son[id][f[id][t]][i], q.push(son[id][t][i]), 0) : (son[id][t][i] = son[id][f[id][t]][i]);
}
ed[id][t] += ed[id][f[id][t]];
}
}
for (int i = 1; i <= n; i++) {
cin >> x, I(x, 0), reverse(x.begin(), x.end()), I(x, 1);
}
B(0), B(1);
for (int i = 1, p = 0; i <= t.size(); i++) {
p = son[0][p][t[i - 1] - 'a'], s[i] = ed[0][p];
}
for (int i = t.size(), p = 0; i; i--) {
p = son[1][p][t[i - 1] - 'a'], ans += s[i - 1] * ed[1][p];
}
3、数学建模维度
复杂度计算:
- 预处理:\(O(|s|)\)
- \(\text{ACAM}\):\(O(|s|\times|t|)\)
- 计算答案:\(O(|t|)\)
- 总复杂度:\(O(|s|\times|t|)\)
\(\text{abc} 416\)
A - Vacation Validation
1、算法思想维度
问题类型:枚举
做题思路:直接枚举每个位置然后判断即可。
2、实现细节维度
题目中的 \(s\) 下标是从 \(1\) 开始的。
核心代码
cin >> n >> l >> r >> s;
for (int i = l; i <= r; i++) {
if (s[i - 1] == 'x') {
return cout << "No\n", 0;
}
}
cout << "Yes\n";
3、数学建模维度
复杂度计算:
- 总复杂度:\(O(n)\)
B - 1D Akari
1、算法思想维度
问题类型:贪心
做题思路:这道题可以贪心求解,每一个连续的 . 区间只能有一个 o,所以在每一个区间的第一个位置变成 o 即可。
2、实现细节维度
核心代码
cin >> s;
for (int i = 0; i < s.size(); i++) {
if (s[i] == '.' && !flag) {
s[i] = 'o', flag = 1;
} else if (s[i] == '#') {
flag = 0;
}
}
cout << s << '\n';
3、数学建模维度
复杂度计算:
- 总复杂度:\(O(n)\)
C - Concat (X-th)
1、算法思想维度
问题类型:\(\text{dfs}\)
做题思路:直接 \(\text{dfs}\) 出 \(n^k\) 中字符串,然后放入一个数组中,最后排序,去除第 \(x\) 个即可。
2、实现细节维度
中间加字符串时,不要忘记初始化为空串。
核心代码
void D(int u) {
if (u > k) {
string sum = "";
for (int i = 1; i <= k; i++) {
sum += s[f[i]];
}
ans[++top] = sum;
return;
}
for (int i = 1; i <= n; i++) {
f[u] = i, D(u + 1);
}
}
D(1);
sort(ans + 1, ans + 1 + top);
cout << ans[x] << '\n';
3、数学建模维度
复杂度计算:
- \(\text{dfs}\):\(O(n^k\times 100)\)
- 排序:\(O(n^k\log n^k\times100)\)
- 总复杂度:\(O(n^k\log n^k\times100)\)
D - Match, Mod, Minimize 2
1、算法思想维度
问题类型:贪心,\(\text{deque}\)
做题思路:把每个 \(b_i\) 变成 \(m-b_i\),这样问题变成了 \(\sum(a_i-b_i)\bmod m\) 的最小值了。所以把按从小到大的顺序把 \(b_i\) 放入 \(\text{deque}\) 中,如果 \(a_i\) 大于等于第一个,则 \(ans\) 加上 a[i]-q.front(),否则加上 a[i]+m-q.back()。
2、实现细节维度
多组数据要初始化,\(b\) 数组要排序。
核心代码
for (int i = 1; i <= n; i++) {
cin >> a[i], a[i] %= m;
}
for (int i = 1; i <= n; i++) {
cin >> b[i], b[i] %= m, b[i] = m - b[i];
}
sort(a + 1, a + 1 + n), sort(b + 1, b + 1 + n);
deque<int> q;
for (int i = 1; i <= n; i++) {
q.push_back(b[i]);
}
for (int i = 1; i <= n; i++) {
if (a[i] >= q.front()) {
ans += a[i] - q.front(), q.pop_front();
} else {
ans += m - (q.back() - a[i]), q.pop_back();
}
}
cout << ans << '\n';
3、数学建模维度
复杂度计算:
- 排序:\(O(n\log n)\)
- 贪心:\(O(n)\)
- 总复杂度:\((n\log n)\)
E - Development
1、算法思想维度
问题类型:\(\text{Floyd}\)
做题思路:直接做 \(\text{Floyd}\) 中间用 \(O(n^2)\) 的方法处理。但由于可以加上机场,所以构建一个虚电,每个机场到虚电距离为 \(t\),虚电到每个机场为 \(0\),这样添加机场可只要 \(O(n^2)\) 了。
2、实现细节维度
虚电到的地方和去虚电的地方也要初始化为正无穷。
核心代码
cin >> n >> m, n++;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
d[i][j] = i == j ? 0 : 1e18;
}
}
for (int i = 1, x, y, z; i <= m; i++) {
cin >> x >> y >> z, d[x][y] = d[y][x] = min(d[x][y], z);
}
cin >> k >> t;
for (int i = 1; i <= k; i++) {
cin >> a[i], d[a[i]][n] = min(d[a[i]][n], t), d[n][a[i]] = 0;
}
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
}
}
for (cin >> q; q--;) {
cin >> op;
if (op == 1) {
cin >> x >> y >> z;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
d[i][j] = min(d[i][j], d[i][x] + z + d[y][j]), d[i][j] = min(d[i][j], d[i][y] + z + d[x][j]);
}
}
} else if (op == 2) {
cin >> a[++k];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
d[i][j] = min(d[i][j], d[i][a[k]] + t + d[n][j]), d[i][j] = min(d[i][j], d[i][n] + d[a[k]][j]);
}
}
} else {
int ans = 0;
for (int i = 1; i < n; i++) {
for (int j = 1; j < n; j++) {
ans += d[i][j] == 1e18 ? 0 : d[i][j];
}
}
cout << ans << '\n';
}
}
3、数学建模维度
复杂度计算:
- \(\text{Floyd}\):\(O(n^3)\)
- 查询次数:\(O(q)\)
- 添加/查询:\(O(n^2)\)
- 总复杂度:\(O(n^3)\)
F - Paint Tree 2
1、算法思想维度
问题类型:树形 \(\text{dp}\),\(\text{dfs}\)
做题思路:定义 \(dp_{x,i,j}\) 表示以节点 \(x\) 为根的子树中,选择了 \(i\) 条路径,且节点 \(x\) 的状态为 \(j\) 时的最大权值和。状态有一下三种:
- \(j=0\):\(x\) 未被覆盖(白色)。
- \(j=1\):\(x\) 被覆盖且是某条路径的端点,且该路径可延伸至父节点。
- \(j=2\):\(x\) 被覆盖且不能与父节点连接。
转移有一下几种:
- 不连接:\(x\) 和 \(y\) 的路径独立,路径数和权值直接相加。
- 连接:又分了三种情况:
- \(x\) 状态 \(0\),\(y\) 状态 \(1\):连接后 \(x\) 变为状态 \(1\),路径数不变。
- \(x\) 状态 \(1\),\(y\) 状态 \(0\):连接后 \(x\) 变为状态 \(2\),路径数不变,需加上 \(y\) 的权值。
- \(x\) 状态 \(1\),\(y\) 状态 \(1\):连接后 \(x\) 变为状态 \(2\),路径数减 \(1\)。
最后答案就为 \(dp_{1,i,j}\) 的最大值。
2、实现细节维度
核心代码
void D(int x, int fa) {
for (int i = 0; i <= k; i++) {
for (int j = 0; j < 3; j++) {
dp[x][i][j] = -1e18;
}
}
dp[x][0][0] = 0, (k >= 1) && (dp[x][1][1] = dp[x][1][2] = 0);
for (int y : e[x]) {
if (y != fa) {
D(y, x);
for (int i = 0; i <= k; i++) {
for (int j = 0; j < 3; j++) {
_dp[i][j] = -1e18;
}
}
for (int i = 0; i <= k; i++) {
for (int j = 0; j < 3; j++) {
if (dp[x][i][j] != -1e18) {
for (int I = 0; I <= k; I++) {
for (int J = 0; J < 3; J++) {
if (dp[y][I][J] != -1e18) {
if (i + I <= k && _dp[i + I][j] < dp[x][i][j] + dp[y][I][J]) {
_dp[i + I][j] = dp[x][i][j] + dp[y][I][J];
}
if (!j && J == 1 && i + I <= k && _dp[i + I][1] < dp[x][i][j] + dp[y][I][J]) {
_dp[i + I][1] = dp[x][i][j] + dp[y][I][J];
}
if (j == 1 && !J && i + I <= k && _dp[i + I][2] < dp[x][i][j] + dp[y][I][J] + a[y]) {
_dp[i + I][2] = dp[x][i][j] + dp[y][I][J] + a[y];
}
if (j == 1 && J == 1 && i + I - 1 >= 0 && i + I - 1 <= k && _dp[i + I - 1][2] < dp[x][i][j] + dp[y][I][J]) {
_dp[i + I - 1][2] = dp[x][i][j] + dp[y][I][J];
}
}
}
}
}
}
}
for (int i = 0; i <= k; i++) {
for (int j = 0; j < 3; j++) {
dp[x][i][j] = _dp[i][j];
}
}
}
}
for (int i = 0; i <= k; i++) {
(dp[x][i][1] != -1e18) && (dp[x][i][1] += a[x]), (dp[x][i][2] != -1e18) && (dp[x][i][2] += a[x]);
}
}
D(1, 0);
for (int j = 0; j <= k; j++) {
for (int s = 0; s < 3; s++) {
ans = max(ans, dp[1][j][s]);
}
}
cout << ans << '\n';
3、数学建模维度
复杂度计算:
- 遍历数量:\(O(n)\)
- 转移(总共):\(O(9\times n\times k^2)\)
- 总复杂度:\(O(9\times n\times k^2)\)

浙公网安备 33010602011771号