弹飞绵羊
题目描述
某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。为了使得游戏更有趣,Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。
输入输出格式
输入格式:
第一行包含一个整数n,表示地上有n个装置,装置的编号从0到n-1。
接下来一行有n个正整数,依次为那n个装置的初始弹力系数。
第三行有一个正整数m,
接下来m行每行至少有两个数i、j,若i=1,你要输出从j出发被弹几次后被弹飞,若i=2则还会再输入一个正整数k,表示第j个弹力装置的系数被修改成k。
输出格式:
对于每个i=1的情况,你都要输出一个需要的步数,占一行。
输入输出样例
说明
对于20%的数据n,m<=10000,对于100%的数据n<=200000,m<=100000
分析:
把每个装置看作一个点,然后连边,每次修改弹力值就可以看作断边和连边,可以用LCT维护。那么查询显然就直接求该点原树到根节点的距离。维护的时候比较方便,很多函数都可以省略。
Code:
1 //It is made by HolseLee on 30th June 2018 2 //Luogu.org P3203 3 #include<bits/stdc++.h> 4 using namespace std; 5 const int N=2e5+7; 6 int n,m,a[N],fa[N],s[N],ch[N][2]; 7 inline int read() 8 { 9 char ch=getchar();int num=0;bool flag=false; 10 while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();} 11 while(ch>='0'&&ch<='9'){num=num*10+ch-'0';ch=getchar();} 12 return flag?-num:num; 13 } 14 inline void pushup(int x) 15 { 16 s[x]=s[ch[x][0]]+s[ch[x][1]]+1; 17 } 18 inline bool isroot(int x) 19 { 20 return (ch[fa[x]][0]!=x)&&(ch[fa[x]][1]!=x); 21 } 22 inline void rotate(int x) 23 { 24 int y=fa[x],z=fa[y]; 25 int k=(ch[y][1]==x); 26 int w=ch[x][k^1]; 27 if(!isroot(y))ch[z][ch[z][1]==y]=x; 28 ch[x][k^1]=y;ch[y][k]=w; 29 if(w)fa[w]=y;fa[y]=x;fa[x]=z; 30 pushup(y); 31 } 32 inline void splay(int x) 33 { 34 while(!isroot(x)){ 35 int y=fa[x],z=fa[y]; 36 if(!isroot(y)) 37 ((ch[y][0]==x)^(ch[z][0]==y))? 38 rotate(y):rotate(x); 39 rotate(x);} 40 pushup(x); 41 } 42 inline void access(int x) 43 { 44 for(int y=0;x;x=fa[y=x]) 45 splay(x),ch[x][1]=y,pushup(x); 46 } 47 int main() 48 { 49 n=read();int x,y,z; 50 for(int i=1;i<=n;i++){ 51 s[i]=1;x=read(); 52 if(i+x<=n)fa[i]=i+x;} 53 m=read(); 54 for(int i=1;i<=m;i++){ 55 x=read();y=read(); 56 if(x==1){ 57 ++y; 58 access(y);splay(y); 59 printf("%d\n",s[y]); 60 } 61 else{ 62 z=read();++y; 63 access(y);splay(y); 64 ch[y][0]=fa[ch[y][0]]=0; 65 if(y+z<=n)fa[y]=y+z; 66 pushup(y);} 67 } 68 return 0; 69 }