题解 P6396 【要有光】
Clever_Jimmy · · 题解
update: 已更正一处错误,不应称之为“虚树”,而是“优化建图”。
我简述了一下题意,若不想看 冗长 的题面的,可以看一下。
题意简述:
给定一个字符串
操作 1:
操作 2:
操作 3:对于非空的
操作 4:对于非空的
操作 5:对于非空的
对于
解题思路:
操作 1
直接连
操作 2
直接连
操作 3
预处理出
操作 4
这个操作,本质上是从
但是,考虑到
考虑优化建图的思想,对每个点建立一个对应的虚点,而虚点 只能往儿子的方向 转移(花费为
那么,连一条
操作 5
操作 5 是独立于上述四个操作的,因为进行完一次操作 5 后 不能再使用 上述四个操作了。
这个倍增一下 level ancestor,结合 dp 转移即可。
设
设
时间复杂度分析
记
显然边的数量是线性的,不难得出最后的时间复杂度为
可以使用配对堆优化 Dijkstra。
参考代码:
Dijkstra 我就不贴了。
#include <bits/stdc++.h>
#define LL long long
const LL inf = 0x3f3f3f3f3f3f3f3fLL;
const int N = 1e6 + 5;
const int M = 2e6 + 5;
int n, m, k, ta, tb, tc, td, te, q, flag = 1;
int cnt, first[N], pos[N];
LL dis[N], f[N];
int lg2, anc[N][40 + 5];
char str[N];
struct EERTREE {
static const int MS = N;
static const int C = 50 + 5;
int n, cntNode, last, s[MS], len[MS], fail[MS], par[MS], ch[MS][C], lst[MS];
int make(int l) {
for(int i = 0; i < C; ++i) ch[cntNode][i] = 0;
len[cntNode] = l;
return cntNode++;
}
int GetFail(int x) {
while(s[n] != s[n - len[x] - 1]) x = fail[x];
return x;
}
void extend(int x) {
s[++n] = x;
int fa = GetFail(last);
if(!ch[fa][x]) {
int now = make(len[fa] + 2);
fail[now] = ch[ GetFail(fail[fa]) ][x];
ch[fa][x] = now;
}
last = ch[fa][x];
par[last] = fa, lst[n] = last;
}
void init() {
n = cntNode = last = 0;
make(0), make(-1);
fail[0] = 1, fail[1] = 0;
s[0] = -1;
}
EERTREE() { init(); }
} t;
int I(char ch) {
if('a' <= ch && ch <= 'z')
return ch - 'a' + 1;
else
return 26 + ch - 'A' + 1;
}
int G(int x) {
return x + t.cntNode - 1;
}
void Build_Graph() {
for(int i = 2; i <= t.cntNode - 1; ++i) {
if(t.fail[i] > 1) Add_Edge(i, t.fail[i], ta), Add_Edge(t.fail[i], i, tb);
else Add_Edge(i, 1, ta), Add_Edge(1, i, tb);
for(int j = 1, fa = t.par[i]; j <= k && fa > 1; ++j, fa = t.par[fa])
Add_Edge(i, fa, tc);
Add_Edge(i, G(i), td);
Add_Edge(G(i), i, 0);
for(int j = 0; j < t.C; ++j)
if(t.ch[i][j])
Add_Edge( G(i), G(t.ch[i][j]), 0 );
}
for( ; 1LL << lg2 < m; ++lg2);
for(int i = 2; i <= t.cntNode - 1; ++i) anc[i][0] = std::max(t.fail[i], 1);
for(int j = 1; j <= lg2; ++j)
for(int i = 2; i <= t.cntNode - 1; ++i)
anc[i][j] = anc[anc[i][j - 1]][j - 1];
}
int Find(int x, int len) {
if(t.len[x] <= len) return x;
for(int j = lg2; j >= 0; --j)
if(anc[x][j] > 1 && t.len[anc[x][j]] > len)
x = anc[x][j];
return anc[x][0];
}
void solve(int l, int r) {
if(l == 1 && r == m) {
puts("0");
return;
}
int p = Find(t.lst[r], r - l + 1);
printf("%lld\n", f[p] + 1LL * (r - l + 1 - t.len[p]) * te + (!flag) * ta);
}
int main() {
scanf("%s", str + 1), m = strlen(str + 1);
memset(first, -1, sizeof(first));
scanf("%d %d %d %d %d %d %d", &k, &ta, &tb, &tc, &td, &te, &q);
for(int i = 1; i <= m; ++i) t.extend( I(str[i]) );
Build_Graph();
Dijkstra(t.last);
for(int i = 1; i <= m; ++i) flag &= (str[i] == str[m - i + 1]);
f[0] = inf, f[1] = dis[1];
for(int i = 2; i <= t.cntNode - 1; ++i)
f[i] = std::min(dis[i], f[t.fail[i]] + 1LL * te * (t.len[i] - t.len[t.fail[i]]));
while(q--) {
int l, r;
scanf("%d %d", &l, &r);
solve(l, r);
}
return 0;
}