【清華集訓2017模擬12.10】迴文串(迴文樹+樹鏈剖分)
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] 是迴文的而且在變換中出現過.
abaab -> abaa -> aba -> abac
注意上述四個串均視為在此次變換中出現,其中 aba 為迴文串,且S[1…3] = S[4…6] = aba,故此次變換的優美度為 6。
由於 NYG 還要忙著出題,這個任務就交給你了。
1 <=n <= 10^5
題解:
這題真的很牛啊,碼了2h,調了2h。
看到迴文串肯定想到迴文樹了。
迴文樹有一個非常重要的性質:
正串和反串的迴文樹的形態是一樣的,這個性質是做這題的基礎。
這題沒有強制線上,可以先把整個串擼出來。
接著正著做一遍迴文樹,反串丟上去跑求對應的編號。
接著考慮每次加字元,就相當於把它對應的點的編號到根的路徑的點的出現次數加1。
這樣當然是錯誤的,我就因為這個卡了1h。
一個點代表的長度如果是len,但是我當前整個串的長度都
所以要往fail鏈上調整,直到當前點代表的長度小於當前串的長度。
對於查詢,可以近似的看作是查兩個點的路徑上的所有點的出現次數*長度,當然,這兩個點也是需要調整長度後的點。
注意如果說這兩個點的lca在串中代表的串再往後一個也是相同的,那麼我在變換中必然不會到達lca,所以這種情況要特判。
對於答案的統計可以用樹鏈剖分+線段樹維護,對於調整長度的操作可以用樹鏈剖分+二分維護,那麼這題的複雜度是
實際上達不到。
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();
}