Description:
NYG 很喜欢研究回文串问题,有一天他想到了这样一个问题:
给出一个字符串 S,现在有 4 种操作:
• addl c :在当前字符串的左端加入字符 c;
• addr c :在当前字符串的右端加入字符 c;
• transl l 1 r 1 l 2 r 2 :取出 S 的两个子串 S[l 1 …r 1 ],S[l 2 …r 2 ],现在 NYG想把前一个字符串变换为后一个字符串,每次操作他可以在前一个字符串的左端插入或删除一个字符,保证 NYG 会使用尽量少的步数进行操作,你需要输出整个操作的优美度。
• transr l 1 r 1 l 2 r 2 :取出 S 的两个子串 S[l 1 …r 1 ],S[l 2 …r 2 ],现在 NYG想把前一个字符串变换为后一个字符串,每次操作他可以在前一个字符串的右端插入或删除一个字符,保证 NYG 会使用尽量少的步数进行操作,你需要输出整个操作的优美度。
设字符串 S 长为 n,且从 1 开始标号,那么 nyg 这样定义一次变换的优美度 p:
定义 S[i…j] 是好的当且仅当 S[i…j] 是回文的而且在变换中出现过.
例如 S = abaabac,现在执行 transr 1 5 4 7,其变换过程为:
abaab -> abaa -> aba -> abac
注意上述四个串均视为在此次变换中出现,其中 aba 为回文串,且S[1…3] = S[4…6] = aba,故此次变换的优美度为 6。
由于 NYG 还要忙着出题,这个任务就交给你了。
1 <=n <= 10^5
题解:
这题真的很牛啊,码了2h,调了2h。
看到回文串肯定想到回文树了。
回文树有一个非常重要的性质:
正串和反串的回文树的形态是一样的,这个性质是做这题的基础。
这题没有强制在线,可以先把整个串撸出来。
接着正着做一遍回文树,反串丢上去跑求对应的编号。
接着考虑每次加字符,就相当于把它对应的点的编号到根的路径的点的出现次数加1。
这样当然是错误的,我就因为这个卡了1h。
一个点代表的长度如果是len,但是我当前整个串的长度都 < <script type="math/tex" id="MathJax-Element-149"><</script>len,那么,加进去的串必然是多了的,有些串现在没有出现,在以后才会出现,只是因为我是对整个串建回文树而已。
所以要往fail链上调整,直到当前点代表的长度小于当前串的长度。
对于查询,可以近似的看作是查两个点的路径上的所有点的出现次数*长度,当然,这两个点也是需要调整长度后的点。
注意如果说这两个点的lca在串中代表的串再往后一个也是相同的,那么我在变换中必然不会到达lca,所以这种情况要特判。
对于答案的统计可以用树链剖分+线段树维护,对于调整长度的操作可以用树链剖分+二分维护,那么这题的复杂度是 O(n(log n)2) 。
实际上达不到。
Code:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
using namespace std;
const int N = 4e5 + 5;
int n, Q, st, en;
int str[N];
int tt[N], next[N], final[N], tpt;
void link(int x, int y) {next[++ tpt] = final[x], tt[tpt] = y, final[x] = tpt;}
int dep[N], top[N], w[N], son[N], siz[N], fa[N], tw;
struct tree {
ll bz, slen, sum;
} t[N * 10];
int wl[N], num[N];
void mkt(int i, int x, int y) {
if(x == y) t[i].slen = wl[x]; else {
int m = x + y >> 1;
mkt(i + i, x, m); mkt(i + i + 1, m + 1, y);
t[i].slen = t[i + i].slen + t[i + i + 1].slen;
}
}
void down(int i) {
if(t[i].bz) {
t[i + i].bz += t[i].bz;
t[i + i].sum += t[i].bz * t[i + i].slen;
t[i + i + 1].bz += t[i].bz;
t[i + i + 1].sum += t[i].bz * t[i + i + 1].slen;
t[i].bz = 0;
}
}
void cadd(int i, int x, int y, int l, int r) {
if(x == l && y == r) {
t[i].bz ++;
t[i].sum += t[i].slen;
} else {
down(i);
int m = x + y >> 1;
if(r <= m) cadd(i + i, x, m, l, r); else
if(l > m) cadd(i + i + 1, m + 1, y, l, r); else
cadd(i + i, x, m, l, m), cadd(i + i + 1, m + 1, y, m + 1, r);
t[i].sum = t[i + i].sum + t[i + i + 1].sum;
}
}
ll find(int i, int x, int y, int l, int r) {
if(x == l && y == r) return t[i].sum;
down(i);
int m = x + y >> 1;
if(r <= m) return find(i + i, x, m, l, r);
if(l > m) return find(i + i + 1, m + 1, y, l, r);
return find(i + i, x, m, l, m) + find(i + i + 1, m + 1, y, m + 1, r);
}
int lca(int x, int y) {
while(top[x] != top[y])
if(dep[top[x]] > dep[top[y]])
x = fa[top[x]]; else y = fa[top[y]];
return dep[x] < dep[y] ? x : y;
}
struct hwt {
int fail[N], to[N][26], len[N], tot, last, n, lat[N];
void First() {
tot = 1;
len[1] = -1; len[0] = 0;
fail[0] = 1;
last = 0;
str[0] = -1;
}
int Getfail(int p) {
while(str[n - len[p] - 1] != str[n]) p = fail[p];
return p;
}
void add(char c) {
n ++;
int cur = Getfail(last);
if(!to[cur][c]) {
int p = ++ tot;
len[p] = len[cur] + 2;
fail[p] = to[Getfail(fail[cur])][c];
to[cur][c] = p;
}
last = to[cur][c];
lat[n] = last;
}
void dg1(int x) {
siz[x] = 1;
for(int i = final[x]; i; i = next[i]) {
int y = tt[i];
fa[y] = x;
dep[y] = dep[x] + 1;
dg1(y);
siz[x] += siz[y];
if(siz[y] > siz[son[x]]) son[x] = y;
}
}
void dg2(int x) {
w[x] = ++ tw;
if(son[x]) top[son[x]] = top[x], dg2(son[x]);
for(int i = final[x]; i; i = next[i]) {
int y = tt[i]; if(y == son[x]) continue;
top[y] = y; dg2(y);
}
}
void pou() {
fo(i, 1, tot) if(lat[i] == 0) lat[i] = 1;
len[1] = 0;
fo(i, 2, tot) {
if(fail[i] == 0) fail[i] = 1;
link(fail[i], i);
}
dep[1] = 1; dg1(1);
top[1] = 1; dg2(1);
fo(i, 1, tot) num[w[i]] = i;
fo(i, 1, tot) wl[w[i]] = len[i];
mkt(1, 1, tot);
}
ll sum(int x, int y) {
ll s = 0;
while(top[x] != top[y])
s += find(1, 1, tot, w[top[x]], w[x]), x = fa[top[x]];
s += find(1, 1, tot, w[y], w[x]);
return s;
}
int tiao(int z, int mi) {
while(len[top[z]] > mi) z = fa[top[z]];
int ans = 0;
for(int l = w[top[z]], r = w[z]; l <= r; ) {
int m = l + r >> 1;
if(len[num[m]] <= mi)
ans = m, l = m + 1; else r = m - 1;
}
return num[ans];
}
} zt, ft;
void addg(int x) {
while(x) cadd(1, 1, zt.tot, w[top[x]], w[x]), x = fa[top[x]];
}
struct Ask {
char s[10];
int l1, r1, l2, r2, x, c;
void read() {
scanf("%s", s);
if(s[0] == 'a') scanf("%d", &c); else scanf("%d %d %d %d", &l1, &r1, &l2, &r2);
if(s[0] == 'a')
if(s[3] == 'l') str[-- st] = c, x = st; else str[++ en] = c, x = en;
}
void Zhuo() {
if(s[0] == 't') {
l1 += st - 1; r1 += st - 1; l2 += st - 1; r2 += st - 1;
if(s[5] == 'l') {
int x = zt.lat[r1], y = zt.lat[r2];
x = zt.tiao(x, r1 - l1 + 1);
y = zt.tiao(y, r2 - l2 + 1);
int z = lca(x, y);
int mi = min(r1 - l1 + 1, r2 - l2 + 1);
ll ans = zt.sum(x, z) + zt.sum(y, z) - zt.sum(z, z);
if(zt.len[z] < mi && str[r1 - zt.len[z]] == str[r2 - zt.len[z]]) ans -= zt.sum(z, z);
printf("%lld\n", ans);
} else {
int x = ft.lat[l1], y = ft.lat[l2];
x = zt.tiao(x, r1 - l1 + 1);
y = zt.tiao(y, r2 - l2 + 1);
int z = lca(x, y);
int mi = min(r1 - l1 + 1, r2 - l2 + 1);
ll ans = zt.sum(x, z) + zt.sum(y, z) - zt.sum(z, z);
if(zt.len[z] < mi && str[l1 + zt.len[z]] == str[l2 + zt.len[z]]) ans -= zt.sum(z, z);
printf("%lld\n", ans);
}
} else {
if(s[3] == 'l') {
st --;
addg(zt.tiao(ft.lat[st], en - st + 1));
} else {
en ++;
addg(zt.tiao(zt.lat[en], en - st + 1));
}
}
}
} aq[N];
void Init() {
scanf("%d %d", &n, &Q);
st = 1e5 + 1, en = 1e5 + n;
fo(i, st, en) scanf("%d", &str[i]);
fo(i, 1, Q) aq[i].read();
fo(i, 1, Q) aq[i].x -= st - 1;
fo(i, 1, en - st + 1) str[i] = str[i + st - 1];
en = en - st + 1;
}
void Build() {
zt.First(); ft.First();
fo(i, 1, en) zt.add(str[i]);
fo(i, 1, en / 2) swap(str[i], str[en - i + 1]);
int x = 1, p = 0;
fo(i, 1, en) {
p ++;
while(str[p] != str[p - zt.len[x] - 1]) x = zt.fail[x];
x = zt.to[x][str[i]];
ft.lat[en - i + 1] = x;
}
fo(i, 1, en) if(!ft.lat[i]) ft.lat[i] = 1;
fo(i, 1, en / 2) swap(str[i], str[en - i + 1]);
zt.pou();
}
void End() {
st = 1e5 + 1 - st + 1; en = st - 1;
fo(i, 1, n) addg(zt.tiao(zt.lat[++ en], i));
fo(i, 1, Q) aq[i].Zhuo();
}
int main() {
freopen("string.in", "r", stdin);
freopen("string.out", "w", stdout);
Init();
Build();
End();
}