2333 弹飞绵羊
2010年省队选拔赛湖南
Lostmonkey发明了一种超级反弹装置。为了在绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始,Lostmonkey在地上沿一条直线摆放 n个反弹装置,并按从前往后的方式将反弹装置依次编号为 0 到 n-1,对 0≤i≤n-1,为第 i 个反弹装置设定了初始弹力系数 ki,当绵羊落到第 i 个反弹装置上时,它将被往后弹出 ki 步,即落到第 i+ki 个反弹装置上,若不存在第i+ki个反弹装置,则绵羊被弹飞。绵羊想知道: 从第i个反弹装置开始, 它被弹出几次 (含被弹飞的那次)后会被弹飞。为使游戏更有趣,Lostmonkey 还可以修改某个反弹装置的弹力系数,但任何时候弹力系数均为正整数。
输入文件第一行是一个整数n,表示地上摆放n个反弹装置,输入文件第二行是用空格隔开的n个正整数k0,k1,…,kn-1,分别表示n个反弹装置的初始弹力系数。输入文件第三行是一个正整数m,表示后面还有m行输入数据。接下来的m行,每行至少有用空格隔开的两个整数i和j,若i=1,则你要输出从第j个反弹装置开始,被弹出几次后会被弹飞;若i=2,则该行有用空格隔开的三个整数i,j和k,表示第j个反弹装置的弹力系数被修改为k。
包含的行数等于输入文件最后m行中i=1的行数。第h行输出一个整数,表示对输入中给出的第h个求弹出次数的问题,基于n个反弹装置当时的弹力系数,求出的弹出次数。
4
1 2 1 1
3
1 1
2 1 1
1 1
2
3
输入的数据保证20%的数据满足n,m≤10000。100%的数据满足n≤200000,m≤100000
题解
块状链表,
维护跳出该块的步数(bushu),所到达的点(finish),对同一块中进行压缩路径(虽然有点不像,但我是这样理解的)。
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #define maxn 200010 5 6 using namespace std; 7 8 int sqrp,n,m,a[maxn],l[1005],r[1005],bushu[maxn],finish[maxn],belong[maxn],gs; 9 10 int find(int x) 11 { 12 int ans=0; 13 for(;;) 14 { 15 ans+=bushu[x]; 16 if (!finish[x]) return ans; 17 x=finish[x]; 18 } 19 } 20 21 int main() 22 { 23 scanf("%d",&n); 24 for (int i=1;i<=n;i++) 25 scanf("%d",&a[i]); 26 sqrp=sqrt(n); 27 if (n%sqrp) gs=n/sqrp+1; 28 else gs=n/sqrp; 29 for (int i=1;i<=gs;i++) 30 l[i]=(i-1)*sqrp+1,r[i]=i*sqrp; 31 r[gs]=n; 32 for (int i=1;i<=n;i++) 33 belong[i]=(i-1)/sqrp+1; 34 for (int i=n;i>0;i--) 35 { 36 if(i+a[i]>n)bushu[i]=1; 37 else if(belong[i]==belong[i+a[i]]) 38 bushu[i]=bushu[i+a[i]]+1,finish[i]=finish[i+a[i]]; 39 else bushu[i]=1,finish[i]=i+a[i]; 40 } 41 scanf("%d",&m); 42 for (int i=1;i<=m;i++) 43 { 44 int aa,bb,cc; 45 scanf("%d",&aa); 46 if (aa==1) 47 scanf("%d",&bb),printf("%d ",find(bb+1)); 48 else 49 { 50 scanf("%d%d",&bb,&cc); 51 a[bb+1]=cc; 52 for(int i=bb+1;i>=l[belong[bb+1]];i--) 53 if(belong[i]==belong[i+a[i]]) 54 bushu[i]=bushu[i+a[i]]+1,finish[i]=finish[i+a[i]]; 55 else bushu[i]=1,finish[i]=i+a[i]; 56 } 57 } 58 return 0; 59 }