1. 程式人生 > >題解 SP4487 【GSS6 - Can you answer these queries VI】

題解 SP4487 【GSS6 - Can you answer these queries VI】

題目大意

給出一個由N個整陣列成的序列A,你需要應用M個操作:

I p x 在p 處插入一個元素 x (解釋:這裡插入是p - 1和p 之間插入)
D p   刪除p 處的一個元素
R p x 修改p 處元素的值為 x 
Q l r 查詢一個區間[l,r]的最大子段和

N <= 100000, M <= 100000

主要思路:FHQ Treap 維護區間

同學們應該都做過GSS系列其他的一些題目,所以維護最大子段和的具體套路這裡就不詳講了。真的不懂的話看這裡的後半篇

我的程式碼中套用了與樓下GKxx大佬的update方式,也就是習慣性的分情況討論。

然後就是FHQ Treap維護區間的問題了。(這裡預設大家會FHQ Treap的維護資料的寫法,如果是Splay黨的話,推薦GKxx大佬的題解)

我們在維護資料時,split是按照值的大小來分的:

inline void split(int rt, int k, int &x, int &y) {
    if(!rt) x = y = 0;
    else {
        if(k > z[rt].w) {
            y = rt, split(z[rt].ch[0], k, x, z[rt].ch[0]);
        } else {
            x = rt, split(z[rt].ch[1], k, z[rt].ch[1], y);
        }
        update(rt);
    }
}

但是對於一個區間,我們總不能在哪個位置就把另外設的一個權值標上位置吧,這樣插入時會有一定的錯誤。

這時我們可以採用類似findkth的方法split,也就是按照這個點的size值來找第幾個。如:

inline void split(int rt, int k, int &x, int &y) {
    if(!rt) x = y = 0;
    else {
        if(k <= z[z[rt].ch[0]].sze) {// k與左兒子的size比較
            y = rt, split(z[rt].ch[0], k, x, z[rt].ch[0]);
        } else {
            x = rt, split(z[rt].ch[1], k - z[z[rt].ch[0]].sze - 1, z[rt].ch[1], y);
            // 這裡有個細節:右子樹的size一定記得把k先減去左子樹的size和這個節點(-1)
        }
         
        update(rt);
    }
}

然後其他的就沒有別的什麼特別的了。在提取區間時只需要split一下r,然後split一下l - 1,分成的三棵子樹中中間的那棵子樹就是維護l ~ r的節點了。

記得開大點陣列!!!否則WA的你天崩地裂(經驗之談)

(我開了大約400000才過,反正200000是過不了的,不太清楚為什麼)

還有,記得開long long才行,因為資料範圍(逃

code:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <map>
#include <vector>
#include <ctime>
using namespace std;
#define go(i, j, n, k) for(int i = j; i <= n; i += k)
#define fo(i, j, n, k) for(int i = j; i >= n; i -= k)
#define mn 400010
#define inf 1 << 30
#define ll long long
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while(ch > '9' || ch < '0') { if(ch == '-') f = -f; ch = getchar(); }
    while(ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}
struct tree{
    int pri, ch[2], sze;
    ll x, sum, lsum, rsum, msum;
} z[mn];
inline void update(int rt)
{
    z[rt].sum = z[rt].x, z[rt].sze = 1;
    if (z[rt].ch[0])
        z[rt].sum += z[z[rt].ch[0]].sum, z[rt].sze += z[z[rt].ch[0]].sze;
    if (z[rt].ch[1])
        z[rt].sum += z[z[rt].ch[1]].sum, z[rt].sze += z[z[rt].ch[1]].sze;
    if (z[rt].ch[0] && z[rt].ch[1]) {
        z[rt].lsum = max(z[z[rt].ch[0]].lsum, z[z[rt].ch[0]].sum + z[rt].x + z[z[rt].ch[1]].lsum);
        z[rt].rsum = max(z[z[rt].ch[1]].rsum, z[z[rt].ch[1]].sum + z[rt].x + z[z[rt].ch[0]].rsum);
        z[rt].msum = max(max(z[z[rt].ch[0]].msum, z[z[rt].ch[1]].msum), z[z[rt].ch[0]].rsum + z[z[rt].ch[1]].lsum + z[rt].x);
    } else if (z[rt].ch[0]) {
        z[rt].lsum = max(max(z[z[rt].ch[0]].lsum, z[z[rt].ch[0]].sum + z[rt].x), 0ll);
        z[rt].rsum = max(z[z[rt].ch[0]].rsum + z[rt].x, 0ll);
        z[rt].msum = max(z[z[rt].ch[0]].msum, z[z[rt].ch[0]].rsum + z[rt].x);
    } else if (z[rt].ch[1]) {
        z[rt].lsum = max(z[z[rt].ch[1]].lsum + z[rt].x, 0ll);
        z[rt].rsum = max(max(z[z[rt].ch[1]].rsum, z[z[rt].ch[1]].sum + z[rt].x), 0ll);
        z[rt].msum = max(z[z[rt].ch[1]].msum, z[z[rt].ch[1]].lsum + z[rt].x);
    } else {
        z[rt].lsum = z[rt].rsum = max(z[rt].x, 0ll);
        z[rt].msum = z[rt].x;
    }
}
int cnt;
inline int newnode(int v = 0) {
    z[++cnt].x = z[cnt].msum = z[cnt].sum = v;
    z[cnt].lsum = z[cnt].rsum = max(v, 0);
    z[cnt].sze = 1;
    z[cnt].pri = rand();
    return cnt;
}
inline int merge(int x, int y) {
    if(!x || !y) return x + y;
    if(z[x].pri < z[y].pri) {
        z[x].ch[1] = merge(z[x].ch[1], y);
        update(x);
        return x;
    } else {
        z[y].ch[0] = merge(x, z[y].ch[0]);
        update(y);
        return y;
    }
}
inline void split(int rt, int k, int &x, int &y) {
    if(!rt) x = y = 0;
    else {
        if(k <= z[z[rt].ch[0]].sze) {
            y = rt, split(z[rt].ch[0], k, x, z[rt].ch[0]);
        } else {
            x = rt, split(z[rt].ch[1], k - z[z[rt].ch[0]].sze - 1, z[rt].ch[1], y);
        }
        update(rt);
    }
}
int n, m, xx, yy, zz, rot;
inline void debug() {
    go(rt, 1, cnt, 1) {
        printf("%d: pri:%d, sze:%d, ch[0]:%d, ch[1]:%d, x:%d\n", rt, z[rt].pri, z[rt].sze, z[rt].ch[0], z[rt].ch[1], z[rt].x);
    }
    printf("\n");
}
int main() {
    srand((unsigned)time(NULL));
    n = read();
    go(i, 1, n, 1) {
        int x = read();
        split(rot, i, xx, yy);
        rot = merge(merge(xx, newnode(x)), yy);
    } 
    m = read();
    go(i, 1, m, 1) {
        char s;
        cin >> s;
        int x = read(), v;
        if(s == 'I') {
            v = read();
            split(rot, x - 1, xx, yy);  
            rot = merge(merge(xx, newnode(v)), yy);
            
        } else if(s == 'D') {
            split(rot, x, xx, zz);
            split(xx, x - 1, xx, yy);
            yy = merge(z[yy].ch[0], z[yy].ch[1]);
            rot = merge(merge(xx, yy), zz);
        } else if(s == 'R') {
            v = read();
            split(rot, x, xx, zz);
            split(xx, x - 1, xx, yy);
            z[yy].x = v;
            z[yy].sum = z[yy].msum = v;
            z[yy].lsum = z[yy].rsum = max(v, 0);
            rot = merge(merge(xx, yy), zz);
        } else if(s == 'Q') {
            v = read();
            split(rot, v, xx, zz);
            split(xx, x - 1, xx, yy);
            printf("%lld\n", z[yy].msum);
            rot = merge(merge(xx, yy), zz);
        }
    }
    return 0;
}

希望可以幫到WA了半天陣列開小或者沒開long long的同學

(我可是因為這兩個調了一天啊QAQ)