[BZOJ 2002] [HNOI2010]弹飞绵羊(Link Cut Tree)

时光怂恿深爱的人放手 提交于 2019-12-06 06:44:00

题面

某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。为了使得游戏更有趣,Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。

分析

首先把所有点编号+1,建立虚拟节点n+1表示被弹飞。如果i+ki>n就连边(i,n+1)否则连边(i,i+ki)。

修改就是先删边再加边

查询其实就是查询x到n+1的路径长度.split(x,n+1)后的子树大小-1即为答案

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm> 
#define maxn 200000
using namespace std;
struct link_cut_tree{
#define lson(x) (tree[x].ch[0])
#define rson(x) (tree[x].ch[1])
#define fa(x) (tree[x].fa)
    struct node{
        int ch[2];
        int fa;
        int sz;
        int revm;
    }tree[maxn+5];
    inline bool is_root(int x){
        return !(lson(fa(x))==x||rson(fa(x))==x);
    }
    inline int check(int x){
        return rson(fa(x))==x;
    }
    void push_up(int x){
        tree[x].sz=tree[lson(x)].sz+tree[rson(x)].sz+1;
    }
    void reverse(int x){
        swap(lson(x),rson(x));
        tree[x].revm^=1;
    }
    void push_down(int x){
        if(tree[x].revm){
            reverse(lson(x));
            reverse(rson(x));
            tree[x].revm=0;
        }
    }
    void push_down_all(int x){
        if(!is_root(x)) push_down_all(fa(x));
        push_down(x);
    }
    void rotate(int x){
        int y=fa(x),z=fa(y),k=check(x),w=tree[x].ch[k^1];
        tree[y].ch[k]=w;
        tree[w].fa=y;
        if(!is_root(y)) tree[z].ch[check(y)]=x;
        tree[x].fa=z;
        tree[x].ch[k^1]=y;
        tree[y].fa=x;
        push_up(y);
        push_up(x);
    }
    void splay(int x){
        push_down_all(x);
        while(!is_root(x)){
            int y=fa(x);
            if(!is_root(y)){
                if(check(x)==check(y)) rotate(y);
                else rotate(x);
            }
            rotate(x);
        }
        push_up(x);
    }
    void access(int x){
        for(int y=0;x;y=x,x=fa(x)){
            splay(x);
            rson(x)=y;
            push_up(x);
        }
    }
    void make_root(int x){
        access(x);
        splay(x);
        reverse(x);
    }
    void split(int x,int y){
        make_root(x);
        access(y);
        splay(y);
    }
    void link(int x,int y){
        make_root(x);
        fa(x)=y;
    }
    void cut(int x,int y){
        split(x,y);
        lson(y)=fa(x)=0;
        push_up(y);
    }
    int query(int x,int y){//查询x到y的路径长度,即split出来的子树大小-1 
        split(x,y);
        return tree[y].sz-1;
    }
}T;

int n,m;
int a[maxn+5];
//虚拟节点n+1表示被弹飞 
inline int get_nex(int x,int k){//找到被弹到的节点 
    if(x+k<=n) return x+k;
    else return n+1;
}

int main(){
    int cmd,x,k;
    scanf("%d",&n);
    for(int i=1;i<=n+1;i++) T.tree[i].sz=1;
//  T.link(4,5);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        T.link(i,get_nex(i,a[i]));
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        scanf("%d",&cmd);
        if(cmd==1){
            scanf("%d",&x);
            x++;
            printf("%d\n",T.query(x,n+1));//查询x到n+1路径长度 
        }else{
            scanf("%d %d",&x,&k);
            x++;
            T.cut(x,get_nex(x,a[x]));
            a[x]=k;
            T.link(x,get_nex(x,a[x]));
        }
    }
     
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!