洛谷P3203 [HNOI2010] 弹飞绵羊 题解
题意:
某天,cxy 发明了一种超级弹力装置,为了在她的绵羊朋友面前显摆,她邀请小绵羊一起玩个游戏。
游戏一开始,cxy 在地上沿着一条直线摆上 \(n\) 个装置,每个装置设定初始弹力系数 \(k_i\),当绵羊达到第 \(i\) 个装置时,它会往后弹 \(k_i\) 步,达到第 \(i+k_i\) 个装置,若不存在第 \(i+k_i\) 个装置,则绵羊被弹飞。
绵羊想知道当它从第 \(i\) 个装置起步时,被弹几次后会被弹飞。为了使得游戏更有趣,cxy 可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。
输入格式:
第一行包含一个整数 \(n\),表示地上有 \(n\) 个装置,装置的编号从 \(0 \sim n-1\)。
接下来一行有 \(n\) 个正整数,依次为那 \(n\) 个装置的初始弹力系数。
第三行有一个正整数 \(m\),表示操作次数。接下来 \(m\) 行每行至少有两个数 \(i,j\)。
若 \(i=1\),你要输出从编号为 \(j\) 的装置出发被弹几次后被弹飞 。
若 \(i=2\),则会再给出一个正整数 \(k\),表示编号为 \(j\) 的弹力装置的系数被修改成 \(k\)。
输出格式:
对于每个 \(i=1\) 的操作,输出一行一个整数表示答案。
数据范围:
\(1\le n \le 2\times 10^5\),\(1\le m \le 10^5\)。
正经人谁写 LCT 啊(
可以发现,如果没有修改,那就是维护一棵树每个节点的深度。
有了修改操作后,每次修改我们看似只能暴力重构树,再暴力查询。
但是根据著名的等式考虑分块。把原序列分成 \(\mathcal{O}(\frac{n}{S})\) 个块,每个块内预处理出每个节点的深度。
对于每个块内的修改,暴力重构整个块内的那棵树,这部分复杂度为 \(\mathcal{O}(S)\) 。
对于查询,直接暴力跳每个块。由于块内已经预处理过了,所以这部分复杂度为 \(\mathcal{O}(\frac{n}{S})\)
取 \(S = \sqrt{n}\) 时,总时间复杂度为 \(\mathcal{O}(m \sqrt{n})\)
代码:
#include <bits/stdc++.h>
using namespace std;
// #define int long long
// #define INF 0x3f3f3f3f3f3f3f3f
void up(int &x, int y) { x < y ? x = y : 0; }
void down(int &x, int y) { x > y ? x = y : 0; }
#define rep(i, a, b) for(int i = (a), i##END = (b); i <= i##END; i++)
#define Rep(i, a, b) for(int i = (a), i##END = (b); i >= i##END; i--)
#define N ((int)(1e6 + 15))
int n, m, S, a[N], to[N], dep[N], blk[N], L[N], R[N];
void build(int l, int r)
{
Rep(i, r, l)
{
if(i + a[i] > R[blk[i]]) { to[i] = i + a[i]; dep[i] = 1; }
else { to[i] = to[i + a[i]]; dep[i] = dep[i + a[i]] + 1; }
}
}
int qry(int x)
{
int r = 0;
for(; x <= n; x = to[x]) r += dep[x];
return r;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
cin >> n; S = sqrt(n + 0.5); m = (n - 1) / S + 1;
rep(i, 1, n) cin >> a[i];
rep(i, 1, n) blk[i] = (i - 1) / S + 1;
rep(i, 1, m) { L[i] = (i - 1) * S + 1, R[i] = i * S; }
// rep(i, 1, m) cout << L[i] << ' ' << R[i] << '\n';
build(1, R[m] = n); int q; cin >> q;
while(q--)
{
static int op, x, y; cin >> op >> x; ++x;
if(op == 1) cout << qry(x) << '\n';
else { cin >> y; a[x] = y; build(L[blk[x]], R[blk[x]]); }
}
return 0;
}
参考文献:
[1] https://www.luogu.com.cn/article/j3yzb34z
题外话: