「BZOJ 2002 && Luogu P3203」弹飞绵羊

某天, Lostmonkey 发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始, Lostmonkey 在地上沿着一条直线摆上 n 个装置,每个装置设定初始弹力系数 \(k_i\) ,当绵羊达到第 i 个装置时,它会往后弹 \(k_i\) 步,达到第 \(i+k_i\) 个装置,若不存在第 \(i+k_i\) 个装置,则绵羊被弹飞。绵羊想知道当它从第 i 个装置起步时,被弹几次后会被弹飞。为了使得游戏更有趣,Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。

BZOJ

Luogu

分析

这题的分块中,每个位置储存两个值,一个是它至少跳到块外的最小次数 cnt[i] ,另一个是它第一次跳到块外的位置 x[i] ,我们要将这两个值预处理出来。

对于询问,就一直跳 x[i] ,直到跳飞,并统计 cnt[i] 就好了;对于修改,和上面预处理的方式一样,由于 j 只会对位于块内而在 j 之前的位置产生影响,所以我们只要处理块内 j 之前的即可。

处理的话,一开始我是 for 循环从小到大,然后在 while 里直接暴力跳的,结果果然 T 飞了, 40pts 。如下:

for (int i = 1; i <= n; ++i) {
	x[i] = i;
	while (x[i] <= R[bl[i]]) {
		x[i] += k[x[i]];
		cnt[i]++;
	}
}

接着把修改操作的处理改成从大到小,没改预处理的,结果 T 的更多了是什么鬼???然后我又把预处理的也改掉,也就比一开始的多过了 2 个点。

最后看了标程,才知道 while 是完全多余的,因为是从大到小枚举,而大的已经处理好了,我们直接用 i+k[i] 位置的直接更新 i 的就好了。

代码

//=========================
//  author:hliwen
//  date:2020.1.16
//=========================
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 200005
#define il inline
#define re register
#define DEBUG puts("ok")
#define tie0 cin.tie(0),cout.tie(0)
#define fastio ios::sync_with_stdio(false)
#define File(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout)
using namespace std;
typedef long long ll;

template <typename T> inline void read(T &x) {
	T f = 1; x = 0; char c;
    for (c = getchar(); !isdigit(c); c = getchar()) if (c == '-') f = -1;
    for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    x *= f;
}

int n, m, blo, sz;
int k[N], bl[N], L[N], R[N], cnt[N], x[N];

int main() {
	int opt, a, b, ans, now;
	read(n);
	sz = sqrt(n);
	for (int i = 1; i <= n; ++i) {
		read(k[i]);
		bl[i] = (i - 1) / sz + 1;
	}
	blo = bl[n];
	for (int i = 1; i <= blo; ++i) {
		L[i] = (i - 1) * sz + 1;
		R[i] = i * sz;
	}
	R[blo] = n;
	for (int i = n; i; --i) {
		if (bl[i] == bl[i+k[i]])
			cnt[i] = cnt[i+k[i]] + 1, x[i] = x[i+k[i]];
		else cnt[i] = 1, x[i] = i + k[i];
	}
	read(m);
	for (int i = 1; i <= m; ++i) {
		read(opt), read(a);
		if (opt == 1) {
			ans = cnt[++a], now = x[a];
			while (now <= n)
				ans += cnt[now], now = x[now];
			printf("%d\n", ans);
		}
		else {
			read(b);
			k[++a] = b;
			for (int j = a; j >= L[bl[a]]; --j) {
				if (bl[j] == bl[j+k[j]])
					cnt[j] = cnt[j+k[j]] + 1, x[j] = x[j+k[j]];
				else cnt[j] = 1, x[j] = j + k[j];
			}
		}
	}
	return 0;
}
posted @ 2020-01-16 22:10  小蒟蒻hlw  阅读(115)  评论(0)    收藏  举报