题解 P4036 【[JSOI2008]火星人】
做法:平衡树(Splay)维护哈希值
这篇题解会具体讲讲 Splay 的过程,供新手参考。
观察插入操作,插入的实际上是一个下标,比如在下标 2 和 3 之间插入一个字母 a,相当于插入了一个位于 2.5 的 a (脑补即可)。对于此类问题,平衡树上的关键字设为下标,而它的值则为它表示的字母。对于一个节点,它的左子树上的节点都是在下标比它小的字符,右子树上的节点都是下标比它大的字符。
先说查询操作。
二分求 Hash 值的做法比较明显,这里主要讲区间查询 Hash。当我们查询区间
那么,A 中所有元素都是下标大于
维护 Hash :s[x].hs=s[s[x].son[0]].hs+(ull)s[x].val*p[s[s[x].son[0]].sz] + s[s[x].son[1]].hs*p[s[s[x].son[0]].sz+1];
其中,
修改操作
这个比较简单,把待修改的节点旋转至根节点后直接修改即可。
插入操作
比如我们需要插入下标为 2.5 的字符,那么我们可以插在这里:
虽然不能真的插入 2.5 但我们只要把节点插在这个位置上无论如何旋转它都能维持下标为 2.5 的性质。
提供代码,仅供参考:
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
int read() {
int x=0, f=1; char ch=' ';
while(!isdigit(ch)) {ch=getchar(); if(ch=='-') f=-1;}
while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48), ch=getchar();
return x*f;
}
int n, root, size;
ull p[1000005];
struct AB{
int val, f, son[2], sz;
ull hs;
}s[1000005];
int get_son(int x) {
return x==s[s[x].f].son[1];
}
void update(int x) {
if(!x) return;
s[x].sz=1;
if(s[x].son[0]) s[x].sz+=s[s[x].son[0]].sz;
if(s[x].son[1]) s[x].sz+=s[s[x].son[1]].sz;
s[x].hs=s[s[x].son[0]].hs+(ull)s[x].val*p[s[s[x].son[0]].sz] + s[s[x].son[1]].hs*p[s[s[x].son[0]].sz+1];
}
void build(int l, int r, int x) {
if(l>r) return;
int mid=(l+r)>>1;
if (mid >= x) s[x].son[1] = mid;
else s[x].son[0] = mid;
s[mid].f = x, s[mid].sz = 1;
if (l==r) return;
build(l, mid-1, mid), build(mid+1, r, mid);
update(mid);
return ;
}
void rotate(int x) {
int fa=s[x].f, ffa=s[fa].f, wson=get_son(x);
s[fa].son[wson]=s[x].son[wson^1];
s[s[fa].son[wson]].f=fa;
s[x].son[wson^1]=fa;
s[fa].f=x;
s[x].f=ffa;
if(ffa) s[ffa].son[s[ffa].son[1]==fa]=x;
update(fa);
update(x);
}
void splay(int x, int goal) {
for(int fa;(fa=s[x].f)!=goal;rotate(x)) {
if(s[fa].f!=goal) {
rotate((get_son(x)==get_son(fa))?fa:x);
}
}
update(x);
if(goal==0) root=x;
}
int find(int x) {
int now=root;
while(1) {
if (s[s[now].son[0]].sz+1 == x) return now;
if (s[s[now].son[0]].sz+1 < x) x -= (s[s[now].son[0]].sz+1), now = s[now].son[1];
else now = s[now].son[0];
}
}
int get_hash(int l, int r) {
int x=find(l), y=find(r+2);
splay(x, 0), splay(y, root);
return s[s[s[root].son[1]].son[0]].hs;
}
char ch[1000005];
int t;
int main() {
scanf("%s", ch+1);
n=strlen(ch+1);
p[0]=1;
for(int i=1;i<=n*2;i++) p[i]=p[i-1]*27ull;
for(int i=2;i<=n+1;i++) {
s[i].val=s[i].hs=ch[i-1]-'a'+1;
}
build(1, n+2, root);
root = (n+3) >> 1, size=n+2;
cin>>t;
while(t--) {
char type[3], ch[3];
scanf("%s", type);
if(type[0]=='Q') {
int x=read(), y=read();
if(x>y) swap(x, y);
int l=0, r=size-y-1, mid;
while(l<r) {
mid=(l+r+1)>>1;
if(get_hash(x, x+mid-1)==get_hash(y, y+mid-1)) l=mid;
else r=mid-1;
}
printf("%d\n", l);
}
if(type[0]=='R') {
int x=read();
scanf("%s", ch);
splay(find(x+1), 0);
s[root].val=ch[0]-'a'+1;
update(root);
}
if(type[0]=='I') {
int u=read();
scanf("%s", ch);
int x=find(u+1), y=find(u+2);
splay(x, 0); splay(y, x);
s[s[root].son[1]].son[0] = ++size;
s[size].f = s[root].son[1];
s[size].val = s[size].hs = ch[0]-'a'+1;
splay(size, 0);
}
}
}