1. 程式人生 > >BZOJ2002 [HNOI2010]彈飛綿羊

BZOJ2002 [HNOI2010]彈飛綿羊

return 需要 題目 fprintf fir 下標 span com 一個

題目藍鏈

Description

\(n\)個彈簧排成一列,每一個彈簧都會給定一個向後彈射的距離,然後需要支持兩個操作

  1. 查詢從某個彈簧開始需要彈多少次後,就會彈到第\(n\)個彈簧之後

  2. 修改某個彈簧的向後彈射距離

Solution

考慮分塊,每一個塊中維護每一個位置要跳多少次才能跳出這個塊,跳出去後是落在那個位置

查詢直接\(\mathcal{O}(\sqrt n)\)一個塊一個塊的模擬去跳,修改就更新修改位置所在塊的信息就可以了

Code

#include <bits/stdc++.h>

using namespace std;

#define fst first
#define snd second
#define squ(x) ((LL)(x) * (x))
#define debug(...) fprintf(stderr, __VA_ARGS__)

typedef long long LL;
typedef pair<int, int> pii;

inline int read() {
    int sum = 0, fg = 1; char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == ‘-‘) fg = -1;
    for (; isdigit(c); c = getchar()) sum = (sum << 3) + (sum << 1) + (c ^ 0x30);
    return fg * sum;
}

const int maxn = 2e5 + 10;
const int B = 400;

int n, m, blks, f[maxn], S[maxn], T[maxn];

int pos(int x) { return x <= n ? (x - 1) / B + 1 : blks + 1; }

int main() {
    freopen("sheep.in", "r", stdin);
    freopen("sheep.out", "w", stdout);

    n = read();
    for (int i = 1; i <= n; i++) f[i] = read() + i;

    blks = (n - 1) / B + 1;
    for (int i = 1; i <= blks; i++) {
        int e = min(i * B, n), tmp = (i - 1) * B;
        for (int j = e; j > tmp; j--)
            if (f[j] <= e) S[j] = S[f[j]] + 1, T[j] = T[f[j]];
            else S[j] = 1, T[j] = f[j];
    }

    m = read();
    while (m--) {
        int op = read();
        if (op == 1) {
            int x = read() + 1, ans = 0;
            while (pos(x) <= blks) ans += S[x], x = T[x];
            printf("%d\n", ans);
        }
        if (op == 2) {
            int x = read() + 1, p = pos(x);
            f[x] = read() + x;
            int tmp = (p - 1) * B, e = min(p * B, n);
            for (int i = x; i > tmp; i--) {
                if (f[i] <= e) S[i] = S[f[i]] + 1, T[i] = T[f[i]];
                else S[i] = 1, T[i] = f[i];
            }
        }
    }

    return 0;
}

Summary

這道題代碼細節是真的多,我打之前沒有想清一些的細節。所以我一邊打一邊想,調了我一個小時

一定要註意區分數組的下標和實際位置

BZOJ2002 [HNOI2010]彈飛綿羊