传送门:洛谷P3203
题目描述
某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。为了使得游戏更有趣,Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。
分析:
由题易知这是一个树形结构,而且是一个森林。题目所求为 点到其所在树的根节点之间的边数 + 1;若是加一个总的根节点,那就是一棵树了。
听说是道LCT的水题(然而并不会),但若是暴力地直接一路向上,统计步数,那么也只能TLE了。对此,可以考虑用分块来优化暴力:将一个块看做整体,记录块内的点到下一个块的步数与位置;至于修改,则将块内的数据相应修改(重构)。
代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#define IL inline
#define open(s) freopen(s".in", "r", stdin); freopen(s".out", "w", stdout);
#define close fclose(stdin); fclose(stdout);
using namespace std;
IL int read()
{
int sum = 0, k = 1;
char c = getchar();
for(;'0' > c || c > '9'; c = getchar())
if(c == '-') k = -1;
for(;'0' <= c && c <= '9'; c = getchar())
sum = sum * 10 + c - '0';
return sum * k;
}
int n, m;
int num[200005];
int block;
int id[200005], to[200005], step[200005];
IL int query(int p)
{
int ans = 0;
for(; id[p] != -1; p = to[p]) ans += step[p];
return ans;
}
IL void update(int p)
{
for(int l = p / block * block, v; p >= l; --p)
{
v = p + num[p];
if(id[p] != id[v])
{
step[p] = 1; to[p] = v;
}else
{
step[p] = step[v] + 1; to[p] = to[v];
}
}
}
int main()
{
open("3203")
n = read(); block = sqrt(n);
memset(id, -1, sizeof(id));
for(int i = 0, k = 0; i < n; ++i)
{
num[i] = read();
id[i] = k;
if(!((i + 1) % block) || i + 1 == n) ++k;
}
for(int i = n - 1, v; i >= 0; --i)
{
v = i + num[i];
if(id[i] != id[v])
{
step[i] = 1; to[i] = v;
}else
{
step[i] = step[v] + 1; to[i] = to[v];
}
}
m = read();
for(int i = 1, x, y; i <= m; ++i)
{
x = read(); y = read();
if(x == 1)
{
printf("%d\n", query(y));
}else
if(x == 2)
{
num[y] = read();
update(y);
}
}
close
return 0;
}