bzoj2002 [Hnoi2010]Bounce 弹飞绵羊
2002: [Hnoi2010]Bounce 弹飞绵羊
Time Limit: 10 Sec Memory Limit: 259 MBSubmit: 12685 Solved: 6455
[Submit][Status][Discuss]
Description
某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。为了使得游戏更有趣,Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。
Input
第一行包含一个整数n,表示地上有n个装置,装置的编号从0到n-1,接下来一行有n个正整数,依次为那n个装置的初始弹力系数。第三行有一个正整数m,接下来m行每行至少有两个数i、j,若i=1,你要输出从j出发被弹几次后被弹飞,若i=2则还会再输入一个正整数k,表示第j个弹力装置的系数被修改成k。对于20%的数据n,m<=10000,对于100%的数据n<=200000,m<=100000
Output
对于每个i=1的情况,你都要输出一个需要的步数,占一行。
Sample Input
4
1 2 1 1
3
1 1
2 1 1
1 1
1 2 1 1
3
1 1
2 1 1
1 1
Sample Output
2
3
3
分析:这道题可以用分块来做.分块的复杂度一般是根号的,这道题的范围按理来说可能过不去,实际上因为分块的效率很高,可以A掉此题.
考虑每一块(点)要记录什么信息.先从暴力的角度来思考,因为分块就是优美的暴力嘛.我们需要记录一个点这一次能够跳到的位置是哪,跳多少次可以跳到外面去.从后往前维护就可以了.如果利用分块的话,就能够把跳出边界这一问题变成跳出每一块这一子问题,pos[i]表示i这个点跳出当前块后落到哪个点,cnt[i]表示i这个点跳多少次后能够跳出块.都挺好维护的,令k=i+a[i](这一次跳到的点),如果k与i在同一个块内,那么cnt[i] = cnt[k] + 1,pos[i] = pos[k],否则cnt[i] = 1,pos[i] = k.查询操作就是模拟一遍跳的过程就好了.
修改操作需要注意,不光是要改x点的cnt和pos值,还要更改处于x左边并且和x位于同一块内的点的pos和cnt值,why?因为x左边的点可能会从x这个点转移而来.那为什么不改其它块内的值呢?因为每个块的信息是相互独立的!
根据每个块的信息独立和将一个暴力问题转化成对于每一个块的子问题,就能轻松解决这道题.
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 200010; int n,belong[maxn],L[maxn],R[maxn],m,cnt[maxn],pos[maxn],a[maxn],block; //cnt代表跳多少次到下一个块,pos代表能跳到的下一个块的位置 int solve(int x) { int res = 0; while (1) { res += cnt[x]; x = pos[x]; if (x == -1) break; } return res; } int main() { scanf("%d",&n); block = sqrt(n); for (int i = 1; i <= n; i++) { scanf("%d",&a[i]); belong[i] = (i - 1) / block + 1; } for (int i = 1; i <= (n - 1) / block + 1; i++) L[i] = (i - 1) * block + 1,R[i] = i * block; for (int i = n; i >= 1; i--) { int k = i + a[i]; if (k > n) { pos[i] = -1; cnt[i] = 1; } else { if (belong[i] == belong[k]) { cnt[i] = cnt[k] + 1; pos[i] = pos[k]; } else { cnt[i] = 1; pos[i] = k; } } } scanf("%d",&m); while (m--) { int op,x,y; scanf("%d",&op); if (op == 1) { scanf("%d",&x); x++; printf("%d\n",solve(x)); } else { scanf("%d%d",&x,&y); x++; a[x] = y; for (int i = x; i >= L[belong[x]]; i--) { if (i + a[i] > n) { pos[i] = -1; cnt[i] = 1; } else { int k = i + a[i]; if (belong[i] == belong[k]) { pos[i] = pos[k]; cnt[i] = cnt[k] + 1; } else { pos[i] = k; cnt[i] = 1; } } } } } return 0; }