题目描述
某天,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;
}