P3203 [HNOI2010]弹飞绵羊
题目描述
某天,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
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=2e5+5; inline int read() { char c=getchar();int num=0; for(;!isdigit(c);c=getchar()); for(;isdigit(c);c=getchar()) num=num*10+c-'0'; return num; } int f[N],c[N][2],s[N]; inline bool nroot(int x) { return c[f[x]][0]==x||c[f[x]][1]==x; } inline void pushup(int x) { s[x]=s[c[x][0]]+s[c[x][1]]+1; } inline void rotate(int x) { int y=f[x],z=f[y],k=c[y][1]==x,w=c[x][!k]; if(nroot(y)) c[z][c[z][1]==y]=x; c[x][!k]=y,c[y][k]=w; if(w) f[w]=y; f[y]=x,f[x]=z; pushup(y); } inline void splay(int x) { int y,z; while(nroot(x)) { y=f[x],z=f[y]; if(nroot(y)) rotate((c[y][0]==x)^(c[z][0]==y)?y:x); rotate(x); } pushup(x); } inline void access(int x) { for(int y=0;x;x=f[y=x]) splay(x),c[x][1]=y,pushup(x); } int opt; int n,m,j,k; int main() { // freopen("testdata.in","r",stdin); // freopen("233.out","w",stdout); n=read(); for(int i=1;i<=n;++i) { s[i]=1; k=read(); if(i+k<=n) f[i]=i+k; //没有被弹飞,连边 } m=read(); while(m--) { opt=read(); if(opt&1) { j=read(),++j; access(j),splay(j); //将j与根打通,然后让j为根,直接查询 printf("%d\n",s[j]); } else { j=read(),k=read(),++j; access(j),splay(j); c[j][0]=f[c[j][0]]=0; //断边 if(j+k<=n) f[j]=j+k; //连边 pushup(j); } } return 0; }