BZOJ2002 [Hnoi2010]Bounce 弹飞绵羊

AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=2002

 

[分析]

  看完LCT的理论分析,找入门题熟悉一下代码的时候...就看了这题

  思路感觉比较明确:因为跳跃就是连边的感觉,而且每个点的出度都等于1,这不就是一棵树吗(当然所有能弹飞的都连到根上)

  

  当然这种树的形态会改变,正好符合动态树的处理范畴.询问的是需要几次弹出去,也就是深度比它浅的有多少,这个也可以用splay做.

 

  但是我抄的是kuangbin的模板.感觉很诡异...因为给你的树的father和平衡树里用的father是一样的....惊恐!旋转splay的时候是可以改变father的值的,也就是每次翻转都会让这棵树的形态变化!

  然而还是可以A,不得不服....

  这便需要反思了...因为每次询问都会打通任督二脉(雾)是Access这个节点[这里利用了根节点的father为0来打通],那么不管你之前怎么改变了树的形态,这个节点最后还是找到了自己到根节点的这条链[虽然现在可能不再是一条链,但会在一颗二叉平衡树里],旋转之后得到的答案也是正确的.而删除某条边的时候,也是先将这个边的发出点Access,也就处理了它后面节点的后顾之忧[断开了],再Splay就让它在树根的位置而且注定没有右子树[之前被断开了],让左子树成为新的平衡树树根就好[前面的节点也没有变化],而添加的时候也只是连虚边,这样操作就是没有问题的.在操作中树的形态不是很重要,但是整个的到根的一条链能通过Access找到,这个很重要.

 

代码[%kuangbin]:

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

inline int in(){
    int x=0,f=1;char ch=getchar();
    while((ch>'9' || ch<'0') && ch!='-') ch=getchar();
    if(ch=='-') ch=getchar(),f=-1;
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}

const int maxn=200010;

struct Node{
    int l,r,f,sz;
    bool rt;
}s[maxn];

int n,m;

void update(int x){
    s[x].sz=s[s[x].l].sz+s[s[x].r].sz+1;
}

void zig(int x){
    int y=s[x].f;
    s[x].f=s[y].f;
    if(s[y].rt) s[x].rt=true,s[y].rt=false;
    else{
        if(s[s[y].f].l==y) s[s[y].f].l=x;
        else s[s[y].f].r=x;
    }
    s[y].l=s[x].r;
    if(s[x].r) s[s[x].r].f=y;
    s[y].f=x,s[x].r=y;
    update(y),update(x);
}

void zag(int x){
    int y=s[x].f;
    s[x].f=s[y].f;
    if(s[y].rt) s[x].rt=true,s[y].rt=false;
    else{
        if(s[s[y].f].l==y) s[s[y].f].l=x;
        else s[s[y].f].r=x;
    }
    s[y].r=s[x].l;
    if(s[x].l) s[s[x].l].f=y;
    s[y].f=x,s[x].l=y;
    update(y),update(x);
}

void Splay(int x){
    int y;
    while(!s[x].rt){
        y=s[x].f;
        if(s[y].rt){ if(s[y].l==x) zig(x);
            else zag(x);}
        else{
            int z=s[y].f;
            if(y==s[z].l){ if(x==s[y].l) zig(y),zig(x);
                else zag(x),zig(x);}
            else{ if(x==s[y].r) zag(y),zag(x);
                else zig(x),zag(x);}
        }
    }
}

/*
    Access(x)表示的是将x与根节点相连的过程
    s[i].r[平衡树中的右节点] 在实际树中的意义是接在i后的点
    last表示的是现在x所在的链需要与之前x所在的链相连,也就需要将原本连接的部分舍去
    last初始为0,可以让x一开始便与后面的链断开
    所以操作就是一步步地往上爬,每次将上次的位置和这次的连上,直到根的过程
*/

void Access(int x){
    int last=0;
    for(;x;x=s[last=x].f){
        Splay(x);
        s[s[x].r].rt=true;
        s[x].r=last;
        s[last].rt=false;
        update(x);
    }
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("2002.in","r",stdin);
    freopen("2002.out","w",stdout);
#endif
    
    int t,ord,x,d;
    
    n=in();
    for(int i=1;i<=n;i++){
        t=in()+i;
        s[i].f=min(t,n+1);
        s[i].rt=true;
        s[i].sz=1;
    }
    s[0].rt=s[n+1].rt=true,s[n+1].sz=1;
    m=in();
    while(m--){
        ord=in();
        if(ord==1){
            x=in()+1;
            Access(x),Splay(x);
            printf("%d\n",s[s[x].l].sz);
        }
        else{
            x=in()+1;d=in();
            Access(x),Splay(x);
            s[s[x].l].f=s[x].f;
            s[s[x].l].rt=true;
            s[x].l=0,s[x].f=0;
            update(x);
            s[x].f=min(n+1,x+d);
        }
    }

    return 0;
}
View Code

 

  后来与同学讨论了一下...对这种记录方法有所改观.

  father其实有两种含义,当rt=true的时候,这个点事实上记录的是这条链的path_father这也就证明了之前Access的复杂度(每次splay之后都是直接跳链)

  当rt=false的时候,father就是单纯的splay中的用法了,用来进行zig,zag,splay操作.

 

好神啊!

posted @ 2016-01-12 19:53  诚叙  阅读(278)  评论(0编辑  收藏  举报