【bzoj2002】[Hnoi2010]Bounce 弹飞绵羊 分块/LCT
题目描述
某天,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。对于20%的数据n,m<=10000,对于100%的数据n<=200000,m<=100000
输出
对于每个i=1的情况,你都要输出一个需要的步数,占一行。
样例输入
4
1 2 1 1
3
1 1
2 1 1
1 1
样例输出
2
3
题解
写这道题时LCT还不会写,于是码了分块。
把n个装置分成√n 个块。设p[i]表示从i开始跳出i所在的块后的第一个位置,超过n则为-1;dis[i]为从i到p[i]的次数。
然后修改只在块内修改,查询只遍历每个块,都只需要√n。
时间复杂度O(n√n)。
数据很弱,不需要优化常数,如果TLE,一定是死循环了。
#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; int to[200010] , p[200010] , dis[200010]; inline int read() { int num = 0; char ch = getchar(); while(ch < '0' || ch > '9') ch = getchar(); while(ch >= '0' && ch <= '9') num = (num << 3) + (num << 1) + ch - '0' , ch = getchar(); return num; } int main() { int n , i , si , m , opt , x , ans; n = read(); si = (int)sqrt(n); for(i = 0 ; i < n ; i ++ ) to[i] = read() + i; for(i = n - 1 ; ~i ; i -- ) { if(to[i] >= n) p[i] = -1 , dis[i] = 1; else if(to[i] >= (i / si + 1) * si) p[i] = to[i] , dis[i] = 1; else p[i] = p[to[i]] , dis[i] = dis[to[i]] + 1; } m = read(); while(m -- ) { opt = read() , x = read(); if(opt == 1) { ans = 0; for(i = x ; ~i ; i = p[i]) ans += dis[i]; printf("%d\n" , ans); } else { to[x] = read() + x; for(i = x ; i >= x / si * si ; i -- ) { if(to[i] >= n) p[i] = -1 , dis[i] = 1; else if(to[i] >= (x / si + 1) * si) p[i] = to[i] , dis[i] = 1; else p[i] = p[to[i]] , dis[i] = dis[to[i]] + 1; } } } return 0; }
17.05.02 更新LCT做法
建立一颗树,每个节点的父节点是它弹一次到达的点,弹出则为n+1(其实可以直接看作森林,然而第一次写保守起见还是建成一棵树)
于是修改操作就是cut+link,查询就是split。
另:这题其实不适合当模板题。
#include <cstdio> #include <cstring> #include <algorithm> #define N 200010 using namespace std; int fa[N] , c[2][N] , si[N] , rev[N] , next[N]; inline int read() { int ret = 0; char ch = getchar(); while(ch < '0' || ch > '9') ch = getchar(); while(ch >= '0' && ch <= '9') ret = (ret << 3) + (ret << 1) + ch - '0' , ch = getchar(); return ret; } void pushup(int k) { si[k] = si[c[0][k]] + si[c[1][k]] + 1; } void pushdown(int k) { if(rev[k]) { int l = c[0][k] , r = c[1][k]; swap(c[0][l] , c[1][l]) , swap(c[0][r] , c[1][r]); rev[l] ^= 1 , rev[r] ^= 1; rev[k] = 0; } } bool isroot(int k) { return c[0][fa[k]] != k && c[1][fa[k]] != k; } void update(int k) { if(!isroot(k)) update(fa[k]); pushdown(k); } void rotate(int x) { int y = fa[x] , z = fa[y] , l = (c[1][y] == x) , r = l ^ 1; if(!isroot(y)) c[c[1][z] == y][z] = x; fa[x] = z , fa[y] = x , fa[c[r][x]] = y , c[l][y] = c[r][x] , c[r][x] = y; pushup(y) , pushup(x); } void splay(int x) { update(x); while(!isroot(x)) { int y = fa[x] , z = fa[y]; if(!isroot(y)) { if((c[0][y] == x) ^ (c[0][z] == y)) rotate(x); else rotate(y); } rotate(x); } } void access(int x) { int t = 0; while(x) splay(x) , c[1][x] = t , pushup(x) , t = x , x = fa[x]; } void makeroot(int x) { access(x) , splay(x); swap(c[0][x] , c[1][x]) , rev[x] ^= 1; } void link(int x , int y) { makeroot(x) , fa[x] = y; } void cut(int x , int y) { makeroot(x) , access(y) , splay(y) , c[0][y] = fa[x] = 0 , pushup(y); } int main() { int n , m , i , opt , x , p; n = read(); for(i = 1 ; i <= n + 1 ; i ++ ) si[i] = 1; for(i = 1 ; i <= n ; i ++ ) p = read() , next[i] = min(i + p , n + 1) , link(i , next[i]); m = read(); while(m -- ) { opt = read() , x = read() + 1; if(opt == 1) makeroot(n + 1) , access(x) , splay(x) , printf("%d\n" , si[c[0][x]]); else p = read() , cut(x , next[x]) , next[x] = min(x + p , n + 1) , link(x , next[x]); } return 0; }