[Link-Cut-Tree][BZOJ2002]彈飛綿羊

題面

Description

某天,Lostmonkey發明了一種超級彈力裝置,爲了在他的綿羊朋友面前顯擺,他邀請小綿羊一塊兒玩個遊戲。遊戲一開始,Lostmonkey在地上沿着一條直線擺上\(n\)個裝置,每一個裝置設定初始彈力系數\(k_i\),當綿羊達到第\(i\)個裝置時,它會往前彈\(k_i\)步,達到第\(i+k_i\)個裝置,若不存在第\(i+k_i\)個裝置,則綿羊被彈飛。綿羊想知道當它從第\(i\)個裝置起步時,被彈幾回後會被彈飛。爲了使得遊戲更有趣,Lostmonkey能夠修改某個彈力裝置的彈力系數,任什麼時候候彈力系數均爲正整數。ios

Input

第一行包含一個整數\(n\),表示地上有\(n\)個裝置,裝置的編號從\(0\)\(n-1\)
接下來一行有\(n\)個正整數,依次爲那\(n\)個裝置的初始彈力系數。
第三行有一個正整數\(m\)
接下來\(m\)行每行至少有兩個數\(i,j\),若\(i=1\),你要輸出從\(j\)出發被彈幾回後被彈飛,若\(i=2\)則還會再輸入一個正整數\(k\),表示第\(j\)個彈力裝置的係數被修改爲\(k\)spa

Output

對於每一個\(i=1\)的狀況,你都要輸出一個須要的步數,佔一行。code

Sample Input

4
1 2 1 1
3
1 1
2 1 1
1 1排序

Sample Output

2
3遊戲

Hint

對於20%的數據\(n,m\le 10000\);
對於100%的數據\(n\le 200000,m\le 100000\);ip


1.如何建樹?

  • 若從這個點\(x\)會被彈飛,連邊\((x,n+1)\)
  • 若從這個點\(x\)不會被彈飛,連邊\((x,x+k_x)\)

根爲\(\textbf{n+1}\)get

以樣例作例子:
input

2.如何詢問?

哈?
因爲splay是按深度關鍵字排序,因此根的左子樹的大小就是要被彈幾回了呀。io

3.如何修改?

把原來的邊刪了在連新的不就完了嗎……class


代碼

#include<iostream>
#include<cstdio>
using namespace std;
int ch[200002][2],fa[200002],siz[200002],num[200002],lazr[200002],cnt,n,q;
inline unsigned rd(){
    unsigned re=0;
    char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9'){
        re=re*10+ch-'0';
        ch=getchar();
    }
    return re;
}
inline bool isroot(int bt){return ch[fa[bt]][0]!=bt&&ch[fa[bt]][1]!=bt;}
inline int drct(int bt){return ch[fa[bt]][1]==bt;}
inline void pushup(int bt){siz[bt]=siz[ch[bt][0]]+siz[ch[bt][1]]+1;}
inline void reverse(int bt){swap(ch[bt][0],ch[bt][1]);lazr[bt]^=1;}
inline void pd(int bt){
    if(lazr[bt]){
        if(ch[bt][0])reverse(ch[bt][0]);
        if(ch[bt][1])reverse(ch[bt][1]);
        lazr[bt]=0;
    }
}
inline void pushdown(int u){
    if(!isroot(u))pushdown(fa[u]);
    pd(u);
}
inline void rotate(int u){
    int f=fa[u],g=fa[f],c=drct(u);
    if(!isroot(f))ch[g][drct(f)]=u;
    fa[u]=g;
    ch[f][c]=ch[u][c^1];
    if(ch[f][c])fa[ch[f][c]]=f;
    ch[u][c^1]=f;
    fa[f]=u;
    pushup(f);
    pushup(u);
}
void splay(int u){
    pushdown(u);
    while(!isroot(u)){
        if(!isroot(fa[u]))rotate(drct(fa[u])==drct(u)?fa[u]:u);
        rotate(u);
    }
}
void access(int u){
    for(int v=0;u;v=u,u=fa[u]){
        splay(u);
        ch[u][1]=v;
        pushup(u);
    }
}
void makeroot(int u){
    access(u);
    splay(u);
    reverse(u);
}
void link(int a,int b){
    makeroot(a);
    fa[a]=b;
}
void cut(int a,int b){
    makeroot(a);
    access(b);
    splay(b);
    ch[b][0]=0;
    fa[a]=0;
    pushup(b);
}
int main(){
    n=rd();
    for(int i=1;i<=n;i++){
        num[i]=rd();
        siz[i]=1;
    }
    for(int i=1;i<=n;i++){
        if(i+num[i]<=n)fa[i]=i+num[i];
        else fa[i]=n+1;
    }
    q=rd();
    for(int i=1;i<=q;i++){
        int opt=rd(),x=rd()+1;
        if(opt==1){
            makeroot(n+1);
            access(x);
            splay(x);
            printf("%d\n",siz[ch[x][0]]);
        }else{
            int y=rd();
            if(x+num[x]<=n)cut(x,x+num[x]);
            else cut(x,n+1);
            num[x]=y;
            if(x+num[x]<=n)link(x,x+num[x]);
            else link(x,n+1);
        }
    }
}
相關文章
相關標籤/搜索