专栏文章
题解:CF461E Appleman and a Game
CF461E题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mir3krww
- 此快照首次捕获于
- 2025/12/04 15:11 3 个月前
- 此快照最后确认于
- 2025/12/04 15:11 3 个月前
补充了部分细节问题。
如果 确定,如何求出其最小操作数?
表示前缀 最少被分成多少段, 能从 转移当且仅当 是 的子串。
注意到 ,因为任意 的方案都能砍掉 后变成 的方案。
因此 的转移点为最小的 满足 是 的子串。
有如下贪心:对于每一段,能延伸就延伸(在 的 sam 上跑,有转移边就走,否则回到根)。
在段数相同的情况下要最小化总长度,这样才能在总长度确定时最大化段数。
设 表示已经拼了 段,以字符 开头且不能再往后延伸一个字符 的最小总长度。
倍增优化:。
问题转化为求 。
其代表的子串在将来一定是接一个 开头的段,并产生 的贡献。
把以 开头的字符串 分成三类:
- 是 的子串且后面不能加 ,在后面接一个 开头的段,恰好产生两个段。
- 是 的子串且后面能加 ,不一定能产生两个段。
- 不是 的子串,已经产生至少两个段了,哪怕新接的段不产生贡献,贡献仍然至少为二。
因此在相同长度下,一三比二更优。
固定长度为 ,第二类有 种,一三加起来有 。
存在一三串当且仅当 ,取 足矣。
对所有后缀建出树高为 的字典树即可求出所有有用的 (可能存在大于 ,但是没用)。
统计答案:从高到低枚举 , 表示 开头,后面接不了 ,在当前答案下的最小长度。
由于有“不能接”这一限制,贪心的让 ,并在最后补一个 (体现在答案上就是加 )。
CPP#include<bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr int N = 1e5 + 5;
int m, t[N * 15][4], idx;
char s[N];
ll n, f[61][4][4], h[4][4], g[4][4];
void dfs(int u, int v, int d, ll& x) {
if(!u) return;
if(!t[u][v]) {
x = min(x, (ll)d);
return;
}
for(int i = 0; i < 4; ++ i) {
dfs(t[u][i], v, d + 1, x);
}
}
main() {
scanf("%lld%s", &n, s);
m = strlen(s);
for(int i = 0; i < m; ++ i) s[i] -= 'A';
for(int i = 0; i < m; ++ i) {
for(int j = i, p = 0; j < m; ++ j) {
if(j - i >= 11) break;
if(!t[p][s[j]]) t[p][s[j]] = ++ idx;
p = t[p][s[j]];
}
}
memset(f, 0x3f, sizeof f);
for(int i = 0; i < 4; ++ i) {
for(int j = 0; j < 4; ++ j) {
dfs(t[0][i], j, 1, f[0][i][j]);
}
}
for(int k = 1; k <= 60; ++ k) {
for(int i = 0; i < 4; ++ i) {
for(int j = 0; j < 4; ++ j) {
for(int x = 0; x < 4; ++ x) {
f[k][i][j] = min(f[k][i][j], f[k - 1][i][x] + f[k - 1][x][j]);
}
}
}
}
ll ans = 0;
for(int k = 60; k >= 0; -- k) {
ll mi = 1e18;
for(int i = 0; i < 4; ++ i) {
for(int j = 0; j < 4; ++ j) {
h[i][j] = 1e18;
for(int x = 0; x < 4; ++ x) {
h[i][j] = min(h[i][j], g[i][x] + f[k][x][j]);
}
mi = min(mi, h[i][j]);
}
}
if(mi < n) {
ans |= 1ll << k;
swap(g, h);
}
}
printf("%lld", ans + 1);
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...