一个神奇的数据结构,可以搞来搞去,还很快?
他的优化就是每次询问一个点的时候,就暴力吧这个点更新为根(用tarjan)给出的更新方式,就可以做到n次操作,平摊下来每次的复杂度是logn,证明的话请见 大牛博客
我还是抱着一定的怀疑的,每次暴力更新,真的不会出问题,然后就去找了一个模板题,结果,真的没有出问题,emmmm,太强了,,,
hdu 1754
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+100;
struct splaytree{
int ch[N][2],fa[N],id[N],pos[N],dat[N],mx[N];
//ch 左右儿子 fa 父亲 id 节点下标 pos i下标所在节点 dat数据 mx子树最大值 root 根
int top,root;
void init(){
top = 1;
root =0;
}
int newnode(int f){
fa[top] = f;
ch[top][0] = ch[top][1] = 0;
mx[top] = 0;
return top++;
}
void update(int x){
mx[x] = max(mx[ch[x][0]],mx[ch[x][1]]);
mx[x] = max(mx[x],dat[x]);
}
void insert(int x,int d){
int tmp = root;
int y = 0;
while(true){
if(tmp == 0){
tmp = newnode(y);
id[tmp] = x;
pos[x] = tmp;
dat[tmp] = d;
if(x < id[y]) ch[y][0] = tmp;
else ch[y][1] = tmp;
splay(tmp,0);
root = tmp;
return;
}
y = tmp;
int dir = x > id[tmp];
tmp = ch[tmp][dir];
}
}
int getson(int x){
return x==ch[fa[x]][1];
}
void rotate(int x){
int dir = !getson(x);
int p = fa[x];
int pf = fa[fa[x]];
ch[pf][fa[x] == ch[pf][1]] = x;
if(ch[x][dir]) fa[ch[x][dir]] = p;
fa[x] = fa[p];
fa[p] = x;
ch[p][!dir] = ch[x][dir];
ch[x][dir] = p;
update(p);
update(x);
//print();
}
void splay(int x,int y){
while(true){
if(fa[x] == y) return;
if(fa[fa[x]] != y){
if(getson(x) == getson(fa[x])) rotate(fa[x]);
else rotate(x);
}
rotate(x);
}
root = x;
}
int get(int l,int r){
splay(pos[l-1],0);
splay(pos[r+1],pos[l-1]);
return mx[ch[pos[r+1]][0]];
}
void set(int x,int y){
splay(pos[x],0);
dat[pos[x]] = y;
mx[pos[x]] = y;
}
void print(){
for(int i = 1;i < top;i ++){
cout << i << ' '<< ch[i][0] << ' '<< ch[i][1] << ' '<< dat[i] << ' '<< mx[i] <<' '<< fa[i] << ' '<<id[i]<< endl;
}
for(int i = 1;i <= 5;i ++){
cout << i << ' '<< pos[i] << endl;
}
}
}spt;
int main(){
int n,m;
while(scanf("%d %d",&n,&m)==2){
spt.init();
spt.insert(0,0);
for(int i = 1;i <= n;i ++){
int now;
scanf("%d",&now);
spt.insert(i,now);
}
spt.insert(n+1,0);
for(int i = 1;i <= m; i++){
char st[10];
int a,b;
scanf("%s %d %d",st,&a,&b);
if(st[0] == 'Q'){
printf("%d\n",spt.get(a,b));
}
else spt.set(a,b);
}
}
return 0;
}
下面讲一下splay的一些应用:
1.查询区间,先把l-1变成跟,再把r+1扭到l-1下面,那么r+1的右儿子就是所有的点了,
2.删除点,先把这个点扭到根,看看儿子的情况,
没有儿子,直接删除,
只有一个儿子,把这个儿子变成根,
有两个儿子,先把这给点扭到根,然后把比这个数小的最大的数扭到他的下面,然后把这个点的右儿子放到这个点的左儿子的右儿子就好了。