题意:
有n个洞,编号1~n,每个洞有一个power,若有球掉入第i个洞,会被弹到第i+power[i]个洞。
两种操作:
1,球掉入第i个洞后,问弹出n个洞前最后掉入的洞的编号,以及弹的次数。
2,修改第i个洞的power值。
很裸的动态树。与这题本质上是相同的:【HYSBZ】1036 树的统计Count
但是,通过分块暴力,可以做到O(n*sqrt(n))。
很显然,将1~n分成sqrt(n)块。
一个很直接的想法,把a块的最后一个直接指向b块的最后一个(a!=b),这样暴力非完整的块,其他块O(1)得到。
但是,可以有更简单的写法,把相同块中每个洞都指向会到达的最后一个洞,并统计其中跳过多少个。
查询就暴力统计下,修改就暴力修改一整块,复杂度都是O(sqrt(n))。
1 #include<cstdio> 2 #include<cmath> 3 #include<algorithm> 4 #define EPS 1e-9 5 #define MAXN 100010 6 using namespace std; 7 int n, block; 8 int power[MAXN], next[MAXN], end[MAXN], cnt[MAXN]; 9 void Update(int x, int y) { 10 if (y > n) { 11 end[x] = x; 12 cnt[x] = 1; 13 next[x] = y; 14 } else { 15 end[x] = end[y]; 16 if (x / block == y / block) { 17 next[x] = next[y]; 18 cnt[x] = cnt[y] + 1; 19 } else { 20 next[x] = y; 21 cnt[x] = 1; 22 } 23 } 24 } 25 void Query(int a, int &x, int &y) { 26 for (x = 0;; a = next[a]) { 27 x += cnt[a]; 28 if (next[a] > n) { 29 y = end[a]; 30 break; 31 } 32 } 33 } 34 int main() { 35 int i, q; 36 int cmd, a, b, x, y; 37 while (~scanf("%d%d", &n, &q)) { 38 block = (int) (ceil(sqrt((double) n)) + EPS); 39 for (i = 1; i <= n; i++) 40 scanf("%d", &power[i]); 41 for (i = n; i; i--) 42 Update(i, i + power[i]); 43 while (q--) { 44 scanf("%d%d", &cmd, &a); 45 if (cmd) { 46 Query(a, x, y); 47 printf("%d %d\n", y, x); 48 } else { 49 scanf("%d", &b); 50 power[a] = b; 51 x = a / block * block; 52 y = x + block; 53 x = max(x, 1); 54 y = min(y, n); 55 for (i = y - 1; i >= x; i--) 56 Update(i, i + power[i]); 57 } 58 } 59 } 60 return 0; 61 }