1. 程式人生 > >【彈飛綿羊_HNOI2010】

【彈飛綿羊_HNOI2010】

else truct span ots 如果能 兩個 line 賦值 dig

題目大意

一共有\(n\)各裝置,從\(0~n-1\)編號,第\(i\)個裝置有一個系數\(A_i\),表示這個裝置能將綿羊向後彈一次,到達第\(i+A_i \text{ }\)個裝置,如果不存在第\(i+A_i\text{ }\)個裝置,則綿羊被彈飛。現在有\(m\)個操作,詢問當綿羊站在第\(i\)個裝置多少次被彈飛或者修改某個裝置的系數。

分析一下

  • 啊LCT是什麽\(\cdots\),啊我不會啊\(\cdots\)
  • 恩,可以單點修改,暴力求解。
  • 誒怎麽跟某種數據結構那麽像?
  • 用塊狀數組來維護一下,就能使修改與求解的時間均衡了。

流程

  • \(n\)個裝置分塊,每個塊的大小為\(\sqrt n\)
    。預處理出每個裝置所在塊的編號,每個塊的邊界。
  • 用兩個大小為\(n\)的數組記錄從第\(i\)個裝置出發,跳到下一個塊的次數,以及跳到下一個塊的哪個位置。
  • 先給\(n\)個裝置全部\(DP\)一遍,求出兩個數組(\(DP\)流程見代碼)。當要查詢時,一個指針從當前指針開始使勁往下一個塊彈,用一個變量記錄彈到下一個塊的次數。
  • 修改的時候只需要在原數組中修改系數,然後在整個塊中用\(DP\)維護一遍,因為當前塊並不影響前面的和後面的塊,反正查詢時也是一個快一個塊找的。
  • 時間復雜度\(O(m \cdot \sqrt n)\)
  • \(A\)啦!

代碼君

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

【彈飛綿羊_HNOI2010】