一共有\(n\)各装置,从\(0~n-1\)编号,第\(i\)个装置有一个系数\(A_i\),表示这个装置能将绵羊向后弹一次,到达第\(i+A_i \text{ }\)个装置,如果不存在第\(i+A_i\text{ }\)个装置,则绵羊被弹飞。现在有\(m\)个操作,询问当绵羊站在第\(i\)个装置多少次被弹飞或者修改某个装置的系数。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cctype>
using namespace std;
inline int read() {
int ch = getchar(), x = 0, op = 1;
while (!isdigit(ch)) {if (ch == ‘-‘) op = -1; ch = getchar();}
while (isdigit(ch)) {x = (x << 1) + (x << 3) + ch - ‘0‘; ch = getchar();}
return x * op;
}
int n, m, block;
int ar[200005], bl[200005], tot[200005], to[200005]; //tot[i]表示在第i个装置上弹几次弹出当前块。
struct BLOCK { //to[i]表示弹出当前块后到达哪个装置。
int l, r;
} base[505];
void DP(int L, int R) {
for (int i = R; i >= L; i--)
if (i + ar[i] > base[bl[i]].r)
tot[i] = 1, to[i] = i + ar[i]; //如果能弹出当前块就直接赋值。
else tot[i] = tot[i + ar[i]] + 1, to[i] = to[i + ar[i]]; //不能的话就把能弹到的装置上的信息赋给它。
} //能弹到的装置保证已经求出了信息。
int main() {
n = read(), block = sqrt(n); //block表示每个快的大小。
for (int i = 1; i <= n; i++)
ar[i] = read();
for (int i = 1; i <= n; i++)
bl[i] = (i - 1) / block + 1; //预处理出每个装置所在的块的编号。
for (int i = 1; i <= bl[n]; i++)
base[i].l = (i - 1) * block + 1, base[i].r = min(i * block, n); //预处理出每个块的左右边界。
DP(1, n); //先求出两个数组。
m = read();
while (m--) {
int choice = read(), x = read() + 1, y;
if (choice == 1) {
int ans = tot[x];
for (int X = to[x]; X <= n; X = to[X])
ans += tot[X]; //当前块到最后一个块进行扫描。
printf("%d\n", ans);
} else {
y = read();
ar[x] = y;
DP(base[bl[x]].l, base[bl[x]].r); // 维护当前块。
}
}
return 0;
}
原文:https://www.cnblogs.com/JackHomes/p/9459582.html