洛谷 P3203 BZOJ 2002 [Hnoi2010]Bounce 弹飞绵羊
题目描述
某天,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不会,或者分块。
分成$\sqrt{n}$块,对于每个格子,维护三个值——$a[i]$第$i$个点的弹力系数,即弹射距离;$nxtn[i]$,弹射到下一个块所需的步数;$nxtp[i]$,从第i个点开始跳,直到离开这个块时的下一个位置(便于统计是一个块一个块地跳)。
输入数据后,从后向前递推求出$nxtp[i]$和$nxtn[i]$。
询问从$x$点开始,要弹飞所需的步数,就从$x$开始,利用$nxtp[i]$暴力跳块,用$nxtn[i]$暴力统计答案,直到飞了,输出答案。
修改第$x$个点的弹力系数,就先把$a[x]$的值修改了,然后从后往前暴力递推$x$所在块的$nxtp[i]$和$nxtn[i]$。
源代码
1 #include<stdio.h> 2 #include<math.h> 3 4 int n,t,m; 5 int a[200010]/*弹力*/,nxtp[200010]/*到下一块的位置*/,nxtn[200010]/*到下一块的步数*/; 6 7 8 int main() 9 { 10 //freopen("test.in","r",stdin); 11 scanf("%d",&n); 12 t=sqrt(n);//块数 13 for(int i=0;i<n;i++) 14 { 15 scanf("%d",a+i); 16 } 17 for(int i=n-1;i>=0;i--)//i/t获得块数 18 { 19 if(i+a[i]>=n) nxtp[i]=n,nxtn[i]=1; 20 else if(i/t!=(i+a[i])/t) nxtp[i]=i+a[i],nxtn[i]=1; 21 else nxtp[i]=nxtp[i+a[i]],nxtn[i]=1+nxtn[i+a[i]]; 22 } 23 scanf("%d",&m); 24 while(m--) 25 { 26 int opt; 27 scanf("%d",&opt); 28 if(opt==1) 29 { 30 int x; 31 scanf("%d",&x); 32 int ans=0; 33 while(x<n) 34 { 35 ans+=nxtn[x]; 36 x=nxtp[x]; 37 } 38 printf("%d\n",ans); 39 } 40 else 41 { 42 int x,y; 43 scanf("%d%d",&x,&y); 44 a[x]=y; 45 for(int i=(x/t+1)*t;i>=x/t*t;i--) 46 { 47 if (i + a[i] >= n) 48 nxtp[i] = n, nxtn[i] = 1; 49 else if (i / t != (i + a[i]) / t) 50 nxtp[i] = i + a[i], nxtn[i] = 1; 51 else 52 nxtp[i] = nxtp[i + a[i]], nxtn[i] = 1 + nxtn[i + a[i]]; 53 } 54 } 55 } 56 return 0; 57 }