【彈飛綿羊_HNOI2010】
阿新 • • 發佈:2018-08-11
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】