lct
hdu2475
题意就是有一些盒子相互嵌套
两个操作:move x y: 把x插入到y下面(y等于0就放入最外面)(还要注意非法情况…例如y在x里)
query x:查询x所在盒子最大的盒子
就说说lct的做法吧…感觉网上都是splay..然而我还没有仔细看【捂脸】
就把嵌套在一起的盒子建成一棵树(开始时边都认为是虚边)
query就时是find..先通道根,然后..找到最左边的一个..也就是根…因为每次access是把下面的连到右子树。而这道题是不需要换根的,换根操作需要rev,然后rev以后根就会变了…然后就不对了【捂脸】
move就是先把x切下来,注意要记录fa[x]和f[x],fa[x]表示树上的他的父节点,f[x]表示真实的情况中x的父亲,因为在旋转中会有问题,所以切的时候先f[x]通到根,然后把x的父节点变为零,也就是把x从他所在的树上切下来了。此时要判断,如果y等于0,也就是不需要把x再连到别处了,就可以返回了;如果是非法情况,当然就不需要在连了,把x转到根,再放回原来的位置就可以了,即x在树上的节点定为事实上的节点;剩下的,就把x的真实和树上的父节点都设为y就可以了
#include <cstdio>
#include <algorithm>
using namespace std;
#define N 55000
int n,m,fa[N],ch[N][2],rev[N],f[N];
char str[10];
bool isroot(int x){return x!=ch[fa[x]][0] && x!=ch[fa[x]][1];}
void pushdown(int x){
if(!rev[x]) return;
rev[ch[x][0]]^=1;rev[ch[x][1]]^=1;rev[x]^=1;
swap(ch[x][0],ch[x][1]);
}
void push(int x){
if(!isroot(x)) push(fa[x]);
pushdown(x);
}
void rotate(int x){
int y=fa[x],z=fa[y],t=ch[y][0]==x;
if(!isroot(y)) ch[z][ch[z][1]==y]=x;
fa[y]=x;fa[x]=z;fa[ch[x][t]]=y;
ch[y][t^1]=ch[x][t];ch[x][t]=y;
}
void splay(int x){
push(x);
while(!isroot(x)){
int y=fa[x];
if(isroot(y)){rotate(x);return;}
if(ch[y][0]==x^ch[fa[y]][0]==y) rotate(x);
else rotate(y);rotate(x);
}
}
void access(int x){
int y=0;
while(x){
splay(x);ch[x][1]=y;
y=x;x=fa[x];
}
}
int find(int x){
access(x);splay(x);
while(ch[x][0]) x=ch[x][0];
return x;
}
void cut(int x){
access(f[x]);
splay(x);fa[x]=0;
}
void move(int x,int y){
cut(x);
if(!y) return;
if(find(y)==x){
splay(x);fa[x]=f[x];
return;
}
fa[x]=f[x]=y;
}
int main(){
int cnt=0;
while(scanf("%d",&n)>0){
if(cnt) printf("\n");cnt++;
for(int i=1;i<=n;i++) scanf("%d",&fa[i]),rev[i]=ch[i][0]=ch[i][1]=0,f[i]=fa[i];
scanf("%d",&m);
for(int i=1;i<=m;i++) {
int x,y;scanf("%s%d",str,&x);
if(str[0]=='M'){
scanf("%d",&y);
move(x,y);
}else printf("%d\n",find(x));
}
}
return 0;
}
splay
填坑【捂脸】,在一个…大佬的鼓励下..我写了splay的做法…有了前人的基础…做的很顺利【捂脸】(当然前人改了一晚上哈哈哈)
就是维护一个dfn序,保证一个盒子套的其他盒子都能是在连续的一段,删除查询也就方便了。求dfn序列就dfs一遍,进的时候给这个点x,出的是个给x+n,所以它所套的就是[x,x+n]
query的话,就直接查这棵树最左边的那个点就好啦,因为最左边的点一定是开始的点(也就是小于等于n)。因为…它能套所有能被它套的(哇..要好好学语文了【捂脸】,我的..表达能力啊呜呜呜呜)
move的话,就找到这段..然后截下来,插入到该插入的那里。注意(同lct),要分类讨论,有的就可以直接返回了…
哦还有,因为…为了省空间…所以…不能建哨兵了(因为不然的话..最坏的情况是每断都要多建两个点【捂脸】)..也以此..导致插入删除与原来的写法有点不同【捂脸】..
嗯splay..也是一个好问题【捂脸】..因为..存根比较困难…所以…splay..就不能是每次转到对于的位置了,就要变成..转到这个点的下面…
#include <cstdio>
#include <cstring>
#define N 100010
struct node{int x,y,next;}edge[N];
int data[N],fa[N],ch[N][2],h[N],f[N],tot,num,n,m,root;
void insert(int x,int y){edge[++num].x=x;edge[num].y=y;edge[num].next=h[x];h[x]=num;}
void dfs(int u){
data[++tot]=u;
for(int i=h[u];i;i=edge[i].next) dfs(edge[i].y);
data[++tot]=u+n;
}
void build(int &p,int l,int r,int f){
int mid=l+r>>1;p=data[mid];fa[p]=f;ch[p][0]=ch[p][1]=0;
if(l==r) return;
if(l<mid) build(ch[p][0],l,mid-1,p);
if(mid<r) build(ch[p][1],mid+1,r,p);
}
void rotate(int &x){
int y=fa[x],z=fa[y],t=ch[y][0]==x;
if(z) ch[z][ch[z][1]==y]=x;
fa[x]=z;fa[y]=x;fa[ch[x][t]]=y;
ch[y][t^1]=ch[x][t];ch[x][t]=y;
}
void splay(int x,int rt){
while(fa[x]!=rt){
int y=fa[x],z=fa[y];
if(z==rt){rotate(x);return;}
if(ch[z][0]==y^ch[y][0]==x) rotate(x);
else rotate(y);rotate(x);
}
}
void query(int x){
splay(x,0);
while(ch[x][0]) x=ch[x][0];
printf("%d\n",x);
}
void move(int x,int y){
if(x==y) return;
splay(x,0);splay(x+n,x);
for(int i=y;i;i=fa[i]) if(i==ch[x+n][0]) return;
int xx=ch[x][0],yy=ch[x+n][1];ch[x][0]=ch[x+n][1]=fa[xx]=fa[yy]=0;
if(xx&&yy){
while(ch[yy][0]) yy=ch[yy][0];
ch[yy][0]=xx;fa[xx]=yy;
}if(!y) return;
splay(y,0);yy=ch[y][1];
while(ch[yy][0]) yy=ch[yy][0];
ch[yy][0]=x;fa[x]=yy;
}
int main(){
int cnt=0;
while(scanf("%d",&n)>0){num=0;
memset(h,0,sizeof(h));
memset(f,0,sizeof(f));
if(cnt) printf("\n");cnt++;
for(int i=1;i<=n;i++){
int x;scanf("%d",&x);
if(x) insert(x,i);
else f[i]=1;
}
for(int i=1;i<=n;i++){
if(f[i]){tot=0;dfs(i);build(root,1,tot,0);}
}scanf("%d",&m);
for(int i=1;i<=m;i++){
int x,y;char str[10];scanf("%s%d",str,&x);
if(str[0]=='M'){
scanf("%d",&y);
move(x,y);
}else query(x);
}
}
return 0;
}