传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2002
这一题除了LCT解法,还有一种更巧妙,代码量更少的解法,就是分块。先想,如果仅仅记录每个节点需要几步可以弹飞,就可以做到O(1)查询O(n)修改;如果仅仅记录每个节点弹力洗漱,就可以做到O(n)查询O(1)修改。这会不会给人一种随机访问数组与链表的感觉呢?如果把n个弹簧分成√n块,记录块里每个弹簧需要几步才能跳出这一个块,并且记录跳出这个块后落到了哪,这样子查询以及修改复杂度都是O(√n)了。
#include <cstdio> #include <cmath> const int maxn = 200005; int n, m, a[maxn], t1, t2, t3, siz, zuihou, kaishi, to[maxn], stp[maxn]; inline int qry(int pos) { int rt = 0; while (~pos) { rt += stp[pos]; pos = to[pos]; } return rt; } inline void upd(int pos, int data) { kaishi = pos / siz * siz; zuihou = (pos / siz + 1) * siz - 1; a[pos] = data; for (int i = pos; i >= kaishi; --i) { if (i + a[i] >= n) { to[i] = -1; stp[i] = 1; } else if (i + a[i] > zuihou) { to[i] = i + a[i]; stp[i] = 1; } else { to[i] = to[i + a[i]]; stp[i] = stp[i + a[i]] + 1; } } } int main(void) { //freopen("in.txt", "r", stdin); scanf("%d", &n); siz = (int)sqrt((float)n + 0.5f); for (int i = 0; i < n; ++i) { scanf("%d", a + i); } for (int i = n - 1; ~i; --i) { zuihou = (i / siz + 1) * siz - 1; if (i + a[i] >= n) { to[i] = -1; stp[i] = 1; } else if (i + a[i] > zuihou) { to[i] = i + a[i]; stp[i] = 1; } else { to[i] = to[i + a[i]]; stp[i] = stp[i + a[i]] + 1; } } scanf("%d", &m); while (m--) { scanf("%d%d", &t1, &t2); if (t1 == 1) { printf("%d ", qry(t2)); } else { scanf("%d", &t3); upd(t2, t3); } } return 0; }