题目描述
某天,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的板题,蒟蒻只能躲在角落默默发抖
- 把所有n个点分成sqrt(n)块
- 对于每个点,记录两个量,一个是它要弹几次才能出它所在的这个块,另外一个是它弹出这个块后到哪个点
-
这样的话,每次询问和修改的复杂度均为O(sqrt(n))
代码
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <cmath> 5 #define N 200010 6 using namespace std; 7 int n,tot,num,Q,ch,k,x,p[N],bz[N],s[N],bel[N],l[1010]; 8 int calc(int x) 9 { 10 int r=0; 11 while (1) 12 { 13 r+=bz[x]; 14 if (!p[x]) break; 15 x=p[x]; 16 } 17 return r; 18 } 19 int main() 20 { 21 scanf("%d",&n),num=sqrt(n),tot=n/num; 22 if (n%num) tot++; 23 for (int i=1;i<=n;i++) scanf("%d",&s[i]); 24 for (int i=1;i<=tot;i++) l[i]=(i-1)*num+1; 25 for (int i=1;i<=n;i++) bel[i]=(i-1)/num+1; 26 for (int i=n;i>0;i--) 27 if (i+s[i]>n) bz[i]=1; 28 else if (bel[i]==bel[i+s[i]]) bz[i]=bz[i+s[i]]+1,p[i]=p[i+s[i]]; 29 else bz[i]=1,p[i]=i+s[i]; 30 scanf("%d",&Q); 31 while (Q--) 32 { 33 scanf("%d%d",&ch,&k),k++; 34 if (ch==1) printf("%d ",calc(k)); 35 else 36 { 37 scanf("%d",&x),s[k]=x; 38 for (int i=k;i>=l[bel[k]];i--) if (bel[i]==bel[i+s[i]]) bz[i]=bz[i+s[i]]+1,p[i]=p[i+s[i]]; else bz[i]=1,p[i]=i+s[i]; 39 } 40 } 41 }