「UOJ2002 BZOJ2002」Bounce 彈飛綿羊【LCT 或 分塊】

#2002. [Hnoi2010]Bounce 彈飛綿羊

Description

某天,LostmonkeyLostmonkey發明了一種超級彈力裝置,爲了在他的綿羊朋友面前顯擺,他邀請小綿羊一起玩個遊戲。遊戲一開始,LostmonkeyLostmonkey在地上沿着一條直線擺上nn個裝置,每個裝置設定初始彈力系數kik_i,當綿羊達到第i個裝置時,它會往後彈ki步,達到第i+kii+k_i個裝置,若不存在第i+kii+k_i個裝置,則綿羊被彈飛。綿羊想知道當它從第ii個裝置起步時,被彈幾次後會被彈飛。爲了使得遊戲更有趣,LostmonkeyLostmonkey可以修改某個彈力裝置的彈力系數,任何時候彈力系數均爲正整數。

Input

第一行包含一個整數nn,表示地上有nn個裝置,裝置的編號從00n1n-1,接下來一行有nn個正整數,依次爲那nn個裝置的初始彈力系數。第三行有一個正整數mm,接下來mm行每行至少有兩個數iji、j,若i=1i=1,你要輸出從jj出發被彈幾次後被彈飛,若i=2i=2則還會再輸入一個正整數kk,表示第jj個彈力裝置的係數被修改成kk。對於20%20\%的數據n,m10000n,m\leq 10000,對於100%100\%的數據n200000,m100000n\leq 200000,m\leq100000

Output

對於每個i=1i=1的情況,你都要輸出一個需要的步數,佔一行。

Sample Input

4
1 2 1 1
3
1 1
2 1 1
1 1

Sample Output

2
3

題意

  • 就是給你一個數組aa,假如你在一個位置ii被彈起來,那麼你將落到i+a[i]i+a[i]點處,兩種操作,一種是修改a[i]a[i],另一種是查詢從位置xx開始跳跳幾次後位置第一次>n>n

題解

  • 顯然LCTLCT做法無腦暴力,直接iimin(i+a[i],n+1)min(i+a[i],n+1)連邊就行了,查詢的時候split(x,n+1)split(x,n+1),這條實鏈的size1size-1就是答案
  • 網上有大佬用分塊做的,留坑待填

代碼

#include<bits/stdc++.h>

using namespace std;
const int maxn=2e5+10;
#define inf 0x3f3f3f3f

namespace LCT{
    int ch[maxn][2],fa[maxn],mark[maxn];
    int val[maxn],siz[maxn];
    inline bool not_root(int x) {return ch[fa[x]][0]==x||ch[fa[x]][1]==x;}
    inline int dir(int x) {return ch[fa[x]][1]==x;}
    inline void add_mark(int x) {swap(ch[x][0],ch[x][1]);mark[x]^=1;}        //將x這顆子樹翻轉
    inline void push_down(int x) {
        if(mark[x]) {
            if(ch[x][0]) add_mark(ch[x][0]);
            if(ch[x][1]) add_mark(ch[x][1]); 
            mark[x]=0;
        }
    }
    inline void push_up(int x) {
        siz[x]=1;
        if(ch[x][0]) siz[x]+=siz[ch[x][0]];
        if(ch[x][1]) siz[x]+=siz[ch[x][1]];   
    }
    inline void pushall(int x) {
        if(not_root(x)) pushall(fa[x]);
        push_down(x);
    }
    inline void rotate(int x){
        int y=fa[x],z=fa[y],k=dir(x);
        if(ch[x][k^1]) fa[ch[x][k^1]]=y;ch[y][k]=ch[x][k^1];
        if(not_root(y)) ch[z][dir(y)]=x;fa[x]=z;
        ch[x][k^1]=y;fa[y]=x;
        push_up(y);
    }
    inline void splay(int x,int goal=0) {
        pushall(x);
        while(not_root(x)) {
            int y=fa[x],z=fa[y];
            if(not_root(y)) {
                if(dir(x)==dir(y)) rotate(y);
                else rotate(x);
            }
            rotate(x);
        }
        push_up(x);
    }
    inline void access(int x) {    //從原樹的根向x拉一條實鏈
        for(int y=0;x;y=x,x=fa[x]) {
            splay(x);ch[x][1]=y;push_up(x);
        }
    }
    inline void make_root(int x) {  //使x成爲原樹的根
        access(x);splay(x);add_mark(x);
    }
    inline int find_root(int x) {   //找到x在原樹中的根
        access(x);splay(x);
        while(ch[x][0]) push_down(x),x=ch[x][0];
        splay(x);
        return x;
    }
    inline void split(int x,int y) {   //拉出一條x->y的實鏈,y爲splay根
        make_root(x);access(y);splay(y);
    }
    inline bool link(int x,int y) {    //連接x與y,若已經在同一顆原樹中,返回0
        make_root(x);
        if(find_root(y)==x) return 0;
        fa[x]=y;return 1;
    } 
    inline bool cut(int x,int y) {
        make_root(x);
        if(find_root(y)!=x||fa[y]!=x||ch[y][0]) return 0;
        fa[y]=ch[x][1]=0;
        push_up(x);
        return 1;
    }
};
using namespace LCT;

int n,m,opt,a[maxn],x,y;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) {
        scanf("%d",&a[i]);
        if(i+a[i]<=n) link(i,i+a[i]);
        else link(i,n+1);
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++) {
        scanf("%d",&opt);
        if(opt==1) {
            scanf("%d",&x);x++;
            split(x,n+1);
            printf("%d\n",siz[n+1]-1);
        }else {
            scanf("%d %d",&x,&y);
            x++;
            if(x+a[x]<=n) cut(x,x+a[x]);
            else cut(x,n+1);
            a[x]=y;
            if(x+a[x]<=n) link(x,x+a[x]);
            else link(x,n+1);
        }
    }
}
~~
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章