「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;
}

猜你喜欢

转载自www.cnblogs.com/hlw1/p/12203519.html
今日推荐