hdu2475 Box


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; 
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值