離線

扣的都要點一下中間才會跑出來喔

離線是啥?

如何離線?

為什麼要學離線呢

怕打比賽到一半網路被駭客打掉

防範駭客的小技巧

但這麼做同樣會沒網路

所以這更加凸顯了離線演算法的重要性

以上都是唬爛啦哈哈

離線 = !在線

在線

離線

等詢問都問完了再一次回答

一些簡單離線題

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
struct ooo{
    int l, r, v, t;
};
int cnt = 0;
array<int, 10004> A;
array<int, 300004> P;
array<int, 1000004> ans;
array<array<int, 2>, 320004> trie;
vector<ooo> Q;
bool cmp(ooo a, ooo b){
    return a.r < b.r;
}
void update(int p, int x, int d, int l){
    P[p] = l;
    if(d < 0) return;
    int c = (x >> d) & 1;
    if(!trie[p][c]) trie[p][c] = ++cnt;
    update(trie[p][c], x, d - 1, l);
}
int query(int p, int x, int d, int l){
   if(d < 0) return x;
   int c = ((x >> d) & 1) ^ 1;
   if(!trie[p][c] || P[trie[p][c]] < l) c ^= 1;
   return (c << d) ^ query(trie[p][c], x, d - 1, l);
}
void run(int n){
    int p = 0;
    for(int i = 0; i <= n; i++){
        while(p < Q.size() && Q[p].r == i){
            ans[Q[p].t] = query(0, Q[p].v ^ A[i], 30, Q[p].l);
            p++;
        }
        update(0, A[i], 30, i);
    }
}
signed main(){
    int n, q, a, l, r, v;
    cin >> n >> q;
    for(int i = 1; i <= n; i++){
        cin >> a;
        A[i] = a ^ A[i - 1];
    }
    for(int i = 1; i <= q; i++){
        cin >> l >> r >> v;
        Q.pb({l, r, v, i});
    }
    sort(Q.begin(), Q.end(), cmp);
    run(n);
    for(int i = 1; i <= q; i++) cout << ans[i] << "\n";
    return 0;
}

#include <bits/stdc++.h>
#define pb push_back
#define int long long
#define mid ((l + r) >> 1)
#define lc (p << 1)
#define rc ((p << 1) | 1)
using namespace std;
struct ooo{
    int l, r, t;
};
array<int, 200004> X, S, ans;
array<int, 800004> seg, hi, tag;
vector<ooo> Q;
bool cmp(ooo a, ooo b){
    return a.l > b.l;
}
void pull(int p){
    seg[p] = seg[lc] + seg[rc];
    hi[p] = max(hi[lc], hi[rc]);
}
void push(int p, int l, int r){
    if(!tag[p]) return;
    seg[lc] = (mid - l + 1) * tag[p];
    seg[rc] = (r - mid) * tag[p];
    hi[lc] = hi[rc] = tag[p];
    tag[lc] = tag[rc] = tag[p];
    tag[p] = 0;
}
int find(int p, int l, int r, int x){
    if(l == r) return l;
    if(x > hi[p]) return r + 1;
    push(p, l, r);
    if(x <= hi[lc]) return find(lc, l, mid, x);
    else return find(rc, mid + 1, r, x);
}
void update(int p, int l, int r, int ql, int qr, int x){
    if(ql > r || qr < l) return;
    if(ql <= l && qr >= r){
        seg[p] = (r - l + 1) * x;
        tag[p] = hi[p] = x;
        return;
    }
    push(p, l, r);
    update(lc, l, mid, ql, qr, x);
    update(rc, mid + 1, r, ql, qr, x);
    pull(p);
}
int query(int p, int l, int r, int ql, int qr){
    if(ql > r || qr < l) return 0;
    if(ql <= l && qr >= r) return seg[p];
    push(p, l, r);
    return query(lc, l, mid, ql, qr) + query(rc, mid + 1, r, ql, qr);
}
void run(int n){
    int p = 0, r;
    for(int i = n; i > 0; i--){
        r = find(1, 1, n, X[i]) - 1;
        update(1, 1, n, i, r, X[i]);
        while(p < Q.size() && Q[p].l == i){
            ans[Q[p].t] = query(1, 1, n, i, Q[p].r) - S[Q[p].r] + S[i - 1];
            p++;
        }
    }
}
signed main(){
    int n, q, l, r;
    cin >> n >> q;
    for(int i = 1; i <= n; i++){
        cin >> X[i];
        S[i] = X[i] + S[i - 1];
    }
    for(int i = 1; i <= q; i++){
        cin >> l >> r;
        Q.pb({l, r, i});
    }
    sort(Q.begin(), Q.end(), cmp);
    run(n);
    for(int i = 1; i <= q; i++) cout << ans[i] << "\n";
    return 0;
}

#include <bits/stdc++.h>
#define int long long
#define pb push_back
#define mid ((l + r) >> 1)
using namespace std;
struct edge{
    int u, v, l, r;
};
int n, com;
array<int, 100004> DSU;
vector<pair<int, int>> tmp;
stack<vector<pair<int, int>>> chg;
int ehash(int u, int v){
    return u * 100001 + v;
}
pair<int, int> dehash(int x){
    return {x / 100001, x % 100001};
}
int fnd(int u){
    if(DSU[u] == u) return u;
    return fnd(DSU[u]);
}
int find(int u){
    tmp.pb({u, DSU[u]});
    if(DSU[u] == u){
        chg.push(tmp);
        tmp.clear();
        return u;
    }
    return DSU[u] = find(DSU[u]);
}
void onion(int u, int v){
    int U = find(u), V = find(v);
    if(U == V) return;
    DSU[V] = U;
    com--;
}
void roll(){
    tmp = chg.top();
    chg.pop();
    for(auto [u, d] : tmp){
        DSU[u] = d;
    }
    tmp.clear();
}
void run(int l, int r, vector<edge> &E){
    int c = com;
    vector<edge> D;
    for(edge e : E){
        if(e.l > r || e.r < l) continue;
        if(e.l <= l && e.r >= r) onion(e.u, e.v);
        else D.pb(e);
    }
    if(l == r) cout << com << " ";
    else{
        run(l, mid, D);
        run(mid + 1, r, D);
    }
    for(edge e : E){
        if(e.l <= l && e.r >= r) roll(), roll();
    }
    com = c;
}
signed main(){
    int m, q, t, a, b, p = 0;
    vector<edge> E;
    map<int, int> M;
    cin >> n >> m >> q;
    com = n;
    for(int i = 1; i <= n; i++) DSU[i] = i;
    while(m--){
        cin >> a >> b;
        if(a > b) swap(a, b);
        M[ehash(a, b)] = 0;
    }
    while(q--){
        cin >> t >> a >> b;
        if(a > b) swap(a, b);
        p++;
        if(t == 1) M[ehash(a, b)] = p;
        else{
            E.pb({a, b, M[ehash(a, b)], p - 1});
            M.erase(ehash(a, b));
        }
    }
    for(auto [e, s] : M){
        auto [u, v] = dehash(e);
        E.pb({u, v, s, p});
    }
    run(0, p, E);
    return 0;
}

是否發現了什麼

離線 = 排序 & 資結

莫隊

拔掉網路線做分塊

在分塊那邊啦

但我時間太多

莫隊在幹嘛

分塊左界

排序右界

掃!

莫隊其實可以修改哦哦哦

把時間當成一個維度

分塊!

把左、右界都分塊

排序時間

掃!

怎麼分塊?

假設每一塊大小都是 \(K\),序列大小為 \(N\),詢問數量為 \(Q\)

總共會有 \(\dfrac{N}{K}\) 塊

因為左右界都要分塊,因此可能性有 \(\dfrac{N^2}{K^2}\) 種

每一筆詢問的左、右界移動次數為 \(K\) 次

每一種分塊可能性都要花 \(Q\) 去掃時間維度

總複雜度 \(O(QK + \dfrac{QN^2}{K^2})\)

怎麼分塊?

砸算幾:

\(QK + \dfrac{QN^2}{K^2} \ge 2\sqrt{QK \times \dfrac{QN^2}{K^2}}\)

\(QK^3 + QN^2 \ge 2QK^2 \sqrt{\dfrac{N^2}{K}}\)

\(\N^2 + K^3 \ge 2\sqrt{N^2K^3}\)

\(N^2 = K^3\) 時最好

\(K = N^\dfrac{2}{3}\)

總複雜度 : \(O(QN^\dfrac{2}{3})\)

是否發現了什麼? 

暴力做是 \(O(QN)\)

跟莫隊只差 \(N^\dfrac{1}{3}\) 

所以其實暴力壓常可能有機會唬爛過去

蹄墓

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
struct ooo{
    int a, b, t;
};
int col = 0, l = 0, r = -1, t, p;
array<int, 140004> C, ans;
array<int, 1000004> S;
array<array<vector<ooo>, 54>, 54> Q;
vector<ooo> R, chg;
void move(int ql, int qr, int qt){
    while(l > ql) col += !S[C[--l]]++;
    while(r < qr) col += !S[C[++r]]++;
    while(l < ql) col -= !--S[C[l++]];
    while(r > qr) col -= !--S[C[r--]];
    while(t < qt){
        t++;
        if(p >= R.size() || t < R[p].t) continue;
        chg.pb({C[R[p].a], R[p].b, R[p].a});
        if(R[p].a >= l && R[p].a <= r){
            col -= !--S[C[R[p].a]];
            col += !S[R[p].b]++;
        }
        C[R[p].a] = R[p].b;
        p++;
    }
}
void roll(){
    reverse(chg.begin(), chg.end());
    for(auto [a, b, x] : chg){
        if(x >= l && x <= r){
            col -= !--S[b];
            col += !S[a]++;
        }
        C[x] = a;
    }
    chg.clear();
}
void MO(vector<ooo> &O){
    t = p = 0;
    roll();
    for(auto [ql, qr, qt] : O){
        move(ql, qr, qt);
        ans[qt] = col;
    }
}
signed main(){
    int n, m, a, b, k;
    char c;
    cin >> n >> m;
    k = ceil(pow(n, 2.0 / 3.0));
    for(int i = 1; i <= n; i++) cin >> C[i];
    for(int i = 1; i <= m; i++){
        cin >> c >> a >> b;
        if(c == 'Q') Q[a / k][b / k].pb({a, b, i});
        else R.pb({a, b, i});
    }
    for(int i = 0; i <= n / k; i++){
        for(int j = i; j <= n / k; j++){
            MO(Q[i][j]);
        }
    }
    for(int i = 1; i <= m; i++){
        if(ans[i]) cout << ans[i] << "\n";
    }
    return 0;
}

回滾莫隊

不能有刪除操作的莫隊

每個詢問左邊都從左界那塊的最右端開始往左加

特判

當左右界同一塊的時候會爆炸

獨立出來暴力跑!

複雜度

跟一般莫隊一樣

\(O(N\sqrt{Q})\)

堤睦

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
struct ooo{
    int l, r, t;
};
struct op{
    int x, ple, pre, f;
};
int l, r, far;
array<int, 200004> A, L, R, ans;
array<vector<ooo>, 200004> Q;
vector<op> chg;
bool cmp(ooo a, ooo b){
    return a.r < b.r;
}
void umbrella(vector<pair<int, int>> &S){
    int lst = 0, cnt = 0;
    for(auto [x, p] : S){
        if(x == lst) A[p] = cnt;
        else A[p] = ++cnt;
        lst = x;
    }
}
void fresh(){
    far = 0;
    for(int &x : L) x = 1e9;
    for(int &x : R) x = 0;
}
void move(int ql, int qr){
    while(r < qr){
        r++;
        L[A[r]] = min(L[A[r]], r);
        R[A[r]] = max(R[A[r]], r);
        far = max(far, R[A[r]] - L[A[r]]);
    }
    while(l > ql){
        l--;
        chg.pb({A[l], L[A[l]], R[A[l]], far});
        L[A[l]] = min(L[A[l]], l);
        R[A[l]] = max(R[A[l]], l);
        far = max(far, R[A[l]] - L[A[l]]);
    }
}
void roll(){
    reverse(chg.begin(), chg.end());
    for(auto [x, ple, pre, f] : chg){
        l++;
        L[x] = ple;
        R[x] = pre;
        far = f;
    }
    chg.clear();
}
void MO(vector<ooo> &O){
    for(auto [ql, qr, qt] : O){
        move(ql, qr);
        ans[qt] = far;
        roll();
    }
}
signed main(){
    int n, m, x, ql, qr, k;
    vector<pair<int, int>> S;
    cin >> n;
    for(int i = 1; i <= n; i++){
        cin >> x;
        S.pb({x, i});
    }
    sort(S.begin(), S.end());
    umbrella(S);
    cin >> m;
    k = ceil((double)n / (sqrt(m)));
    fresh();
    for(int i = 1; i <= m; i++){
        cin >> ql >> qr;
        if(ql / k == qr / k){
            l = qr + 1, r = qr;
            move(ql, qr);
            ans[i] = far;
            roll();
        }else Q[ql / k].pb({ql, qr, i});
    }
    for(int i = 0; i <= n / k; i++){
        sort(Q[i].begin(), Q[i].end(), cmp);
        fresh();
        l = min(n + 1, i * k + k);
        r = l - 1;
        MO(Q[i]);
    }
    for(int i = 1; i <= m; i++) cout << ans[i] << "\n";
    return 0;
}

整體二分搜

拔掉網路線做二分搜

當你覺得一個一個二分搜太慢的時候就可以全部一起二分搜

只有一筆詢問的作法

戳一個數字,看區間內有幾個數字比他小,然後決定往比較小或大的數字戳

\(O(NlogC)\)

很多筆詢問怎麼辦?

統一戳一個數字,然後決定要把每個詢問往比較小或大那邊丟

加入修改

把改值想成刪除和插入,就可以二分搜了耶

維護序列

砸BIT

複雜度

序列長度為 \(N\),詢問數為 \(Q\)

每筆詢問二分搜時間 \(O(NlogNlogC)\)

全部詢問一起二分搜時間 \(O((Q + N)logNlogC)\)

結論 : \(Q \times N = Q + N\)

\(\times = +\)

#include <bits/stdc++.h>
#define int long long
#define pb push_back
#define mid ((l + r) >> 1)
using namespace std;
struct ooo{
    int t, l, r, k, p;
};
array<int, 10004> ans;
array<int, 50004> A, BIT;
void update(int p, int v){
    for(; p < 50004; p += p & -p) BIT[p] += v;
}
int query(int p){
    int sum = 0;
    for(; p; p -= p & -p) sum += BIT[p];
    return sum;
}
void BIS(int l, int r, vector<ooo> &Q){
    if(Q.empty() || l == r){
         for(auto [t, ql, qr, k, p] : Q){
            if(t) ans[p] = l;
         }
         return;
    }
    vector<ooo> L, R;
    for(auto [t, ql, qr, k, p] : Q){
        if(t){
            if(query(qr) - query(ql - 1) >= k) L.pb({t, ql, qr, k, p});
            else R.pb({t, ql, qr, k - query(qr) + query(ql - 1), p});
        }else{
            if(qr <= mid){
                update(ql, k);
                L.pb({t, ql, qr, k, p});
            }else R.pb({t, ql, qr, k, p});
        }
    }
    for(auto [t, ql, qr, k, p] : L){
        if(!t) update(ql, -k);
    }
    BIS(l, mid, L);
    BIS(mid + 1, r ,R);
}
signed main(){
    int t, n, q, p, l, r, k;
    vector<ooo> Q;
    cin >> t;
    for(int &a : ans) a = 1ll << 31;
    while(t--){
        Q.clear();
        cin >> n >> q;
        for(int i = 1; i <= n; i++){
            cin >> A[i];
            Q.pb({0, i, A[i], 1, 0});
        }
        for(int i = 1; i <= q; i++){
            cin >> p >> l >> r;
            if(p == 1){
                cin >> k;
                Q.pb({1, l, r, k, i});
            }else if(p == 2){
                Q.pb({0, l, A[l], -1, 0});
                Q.pb({0, l, r, 1, 0});
                A[l] = r;
            }else{
                ans[i] = 7122;
            }
        }
        BIS(-(1ll << 31), 1ll << 31, Q);
        for(int &a : ans){
            if(a < 1ll << 31){
                cout << a << "\n";
                a = 1ll << 31;
            }
        }
    }
    return 0;
}

啼木

#include <bits/stdc++.h>
#define int long long
#define pb push_back
#define mid ((l + r) >> 1)
using namespace std;
struct ooo{
    int t, l, r, c, p;
};
int pl, pr;
array<int, 100004> pre, V, G, ans, BIT;
array<ooo, 200004> Q, L, R;
bool cmp(ooo a, ooo b){
    if(a.l == b.l) return a.t < b.t;
    return a.l > b.l;
}
void update(int p, int x){
    if(!p) return;
    for(; p < 100004; p += p & -p) BIT[p] += x;
}
int query(int p){
    int sum = 0;
    for(; p; p -= p & -p) sum += BIT[p];
    return sum;
}
void BIS(int l, int r, int tl, int tr){
    if(Q.empty() || l == r){
        for(int i = tl; i <= tr; i++){
            auto [t, ql, qr, c, p] = Q[i];
            if(!t) ans[c] = l;
        }
        return;
    }
    int tmid;
    pl = pr = 0;
    for(int i = tl; i <= tr; i++){
        auto [t, ql, qr, c, p] = Q[i];
        if(p > mid) continue;
        if(t){
            update(qr, c);
            update(ql - 1, -c);
        }else G[c] += query(100000) - query(qr - 1);
    }
    for(int i = tl; i <= tr; i++){
        auto [t, ql, qr, c, p] = Q[i];
        if(t){
            if(p <= mid) L[pl++] = {t, ql, qr, c, p};
            else R[pr++] = {t, ql, qr, c, p};
        }else{
            if(G[c] >= V[c]) L[pl++] = {t, ql, qr, c, p};
            else{
                if(!ql) V[c] -= G[c];
                R[pr++] = {t, ql, qr, c, p};
            }
            if(!ql) G[c] = 0;
        }
    }
    tmid = tl + pl - 1;
    for(int i = 0; i < pl; i++){
        auto [t, ql, qr, c, p] = L[i];
        if(!t) continue;
        update(qr, -c);
        update(ql - 1, c);
    }
    for(int i = 0; i < pl; i++){
        Q[tl + i] = L[i];
    }
    for(int i = 0; i < pr; i++){
        Q[tmid + i + 1] = R[i];
    }
    BIS(l, mid, tl, tmid);
    BIS(mid + 1, r, tmid + 1, tr);
}
signed main(){
    int n, m, q, l, r, c, p = 0;
    cin >> n >> m >> q;
    for(int i = 1; i <= m; i++){
        cin >> c;
        Q[p++] = {0, pre[c], i, c, 0};
        pre[c] = i;
    }
    for(int i = 1; i <= n; i++) cin >> V[i];
    for(int i = 1; i <= q; i++){
        cin >> l >> r >> c;
        Q[p++] = {1, l, r, c, i};
    }
    sort(Q.begin(), Q.begin() + p, cmp);
    BIS(1, q + 1, 0, p - 1);
    for(int i = 1; i <= n; i++){
        cout << (ans[i] <= q && ans[i]? ans[i] : -1) << "\n";
    }
    return 0;
}

CDQ分治

拔掉網路線做分治

一種分治

一維偏序

排序!

二維偏序

砸二維線段樹(X

二維偏序

掃描線 :

照 \(x\) 排序,用BIT維護 \(y\) 前綴和

\(O(NlogN)\)

三維偏序

二維線段樹 + 掃描線?

三維偏序

對 \(z\) 分治,照 \(x\) 排序,用BIT維護 \(y\) 前綴和

\(O(Nlog^2N)\)

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
struct ooo{
    int x, y, z, t;
};
array<int, 100004> ans, BIT;
bool cmp(ooo a, ooo b){
    if(a.z != b.z) return a.z > b.z;
    return a.y < b.y;
}
void update(int p, int x){
    for(; p < 100004; p += p & -p) BIT[p] += x;
}
int query(int p){
    int sum = 0;
    for(; p; p -= p & -p) sum += BIT[p];
    return sum;
}
void CDQ(vector<ooo> &Q){
    if(Q.size() <= 1) return;
    int p = 0;
    vector<ooo> L, R;
    for(int i = 0; i < Q.size(); i++){
        if(i < Q.size() / 2) L.pb(Q[i]);
        else R.pb(Q[i]);
    }
    CDQ(L), CDQ(R);
    Q.clear();
    for(auto [rx, ry, rz, rt] : R){
        while(p < L.size()){
            auto [lx, ly, lz, lt] = L[p];
            if(rx >= lx) break;
            update(ly, 1);
            Q.pb(L[p++]);
        }
        ans[rt] += query(100000) - query(ry);
        Q.pb({rx, ry, rz, rt});
    }
    for(int i = 0; i < p; i++){
        auto [lx, ly, lz, lt] = L[i];
        update(ly, -1);
    }
    while(p < L.size()) Q.pb(L[p++]);
}
signed main(){
    int n, x, y, z;
    vector<ooo> Q;
    cin >> n;
    for(int i = 0; i < n; i++){
        cin >> x >> y >> z;
        Q.pb({x, y, z, i});
    }
    sort(Q.begin(), Q.end(), cmp);
    CDQ(Q);
    for(int i = 0; i < n; i++) cout << ans[i] << "\n";
    return 0;
}

跟離線什麼關係?

逆序數對

\(x = \) 位置,\(v = \) 值

若 \((i, j)\) 為逆序數對 :

\(x_i < x_j \ or \ x_j < x_i\)

\(v_i > v_j \ or \ v_j > v_i\)

看起來就很二維偏序

把時間變成一個維度

題目是刪除元素,感覺很困難

因此可以倒著想,刪除就變成加入了,好像比較好 (?

變成三維偏序

\(t = \) 加入時間,\(x = \) 位置,\(v = \) 值

若 \((i, j)\) 為逆序數對 :

\(t_i \le t_j\)

\(x_i < x_j \ or \ x_j < x_i\)

\(v_i > v_j \ or \ v_j > v_i\)

#include <bits/stdc++.h>
#define pb push_back
#define int long long
using namespace std;
struct ooo{
    int t, x, v;
};
array<int, 100004> BIT, ans;
array<vector<int>, 100004> S;
map<int, int> M;
bool cmp(ooo a, ooo b){
    if(a.t != b.t) return a.t < b.t;
    return a.x < b.x;
}
void update(int p, int x){
    for(; p < 100004; p += p & -p) BIT[p] += x;
}
int query(int p){
    int sum = 0;
    for(; p; p -= p & -p) sum += BIT[p];
    return sum;
}
void CDQ(vector<ooo> &Q){
    if(Q.size() <= 1) return;
    int p = 0;
    vector<ooo> L, R;
    for(int i = 0; i < Q.size(); i++){
        if(i < Q.size() / 2) L.pb(Q[i]);
        else R.pb(Q[i]);
    }
    CDQ(L), CDQ(R);
    Q.clear();
    for(auto [rt, rx, rv] : R){
        while(p < L.size()){
            auto [lt, lx, lv] = L[p];
            if(rx < lx) break;
            update(lv, 1);
            Q.pb(L[p++]);
        }
        ans[rt] += query(100000) - query(rv) - query(rv - 1);
        Q.pb({rt, rx, rv});
    }
    while(p < L.size()){
        auto [lt, lx, lv] = L[p];
        update(lv, 1);
        Q.pb(L[p++]);
    }
    for(auto [rt, rx, rv] : R) ans[rt] += query(rv - 1);
    for(auto [lt, lx, lv] : L) update(lv, -1);
}
signed main(){
    int n, m, d, v, cnt = 1;
    vector<ooo> Q;
    cin >> n >> m;
    for(int i = 0; i < n; i++){
        cin >> v;
        Q.pb({0, i, v});
        M[v] = 0;
    }
    for(auto &[x, c] : M) c = cnt++;
    for(int i = 0; i < n; i++){
        Q[i].v = M[Q[i].v];
        S[Q[i].v].pb(i);
    }
    for(int i = m; i; i--){
        cin >> d;
        for(int s : S[M[d]]) Q[s].t = i;
    }
    sort(Q.begin(), Q.end(), cmp);
    CDQ(Q);
    for(int i = 1; i <= m; i++) ans[i] += ans[i - 1];
    for(int i = m; i; i--) cout << ans[i] << "\n";
    return 0;
}

提募

#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
struct ooo{
    int p, l, r, v, t;
};
array<int, 100004> V, E, D, bar, las, ans;
array<set<int>, 100004> S;
void update(array<int, 100004> &BIT, int p, int x){
    if(!p){
        BIT[0] += x;
        return;
    }
    for(; p < 100004; p += p & -p) BIT[p] += x;
}
int query(array<int, 100004> &BIT, int p){
    int sum = BIT[0];
    for(; p; p -= p & -p) sum += BIT[p];
    return sum;
}
void add(vector<ooo> &Q, int s, int t, int p, int x){
    if(s){
        auto l = --S[s].find(p), r = ++S[s].find(p);
        Q.pb({1, *l, p, -s, x});
        if(r != S[s].end()){
            Q.pb({1, p, *r, -s, x});
            Q.pb({1, *l, *r, s, x});
        }
        S[s].erase(p);
    }
    if(t){
        S[t].insert(p);
        auto l = --S[t].find(p), r = ++S[t].find(p);
        Q.pb({1, *l, p, t, x});
        if(r != S[t].end()){
            Q.pb({1, *l, *r, -t, x});
            Q.pb({1, p, *r, t, x});
        }
    }
}
void CDQ(vector<ooo> &Q){
    if(Q.size() <= 1) return;
    int p = 0;
    vector<ooo> L, R;
    for(int i = 0; i < Q.size(); i++){
        if(i < Q.size() / 2) L.pb(Q[i]);
        else R.pb(Q[i]);
    }
    CDQ(L), CDQ(R);
    Q.clear();
    for(auto [rp, rl, rr, rv, rt] : R){
        while(p < L.size()){
            auto [lp, ll, lr, lv, lt] = L[p];
            if(lr > rr) break;
            if(lp == 1){
                update(bar, ll, lv);
                update(bar, lr, -lv);
            }else if(lp == 2) update(las, ll, lv);
            Q.pb(L[p++]);
        }
        Q.pb({rp, rl, rr, rv, rt});
        if(rp == 3) ans[rt] += query(las, 100000) - query(las, rl - 1) + query(bar, rl - 1);
    }
    for(int i = 0; i < p; i++){
        auto [lp, ll, lr, lv, lt] = L[i];
        if(lp == 1){
            update(bar, ll, -lv);
            update(bar, lr, lv);
        }else if(lp == 2) update(las, ll, -lv);
    }
    while(p < L.size()) Q.pb(L[p++]);
}
signed main(){
    int n, q, t, l, r, p, v;
    vector<ooo> Q;
    cin >> n >> q;
    for(int i = 1; i <= n; i++) S[i].insert(0);
    for(int i = 1; i <= n; i++){
        cin >> V[i];
        l = *--S[V[i]].upper_bound(i);
        add(Q, 0, V[i], i, 0);
    }
    for(int i = 1; i <= q; i++){
        cin >> t;
        if(t == 1){
            cin >> p >> v;
            add(Q, V[p], v, p, i);
            V[p] = v;
        }else if(t == 2){
            cin >> l >> r >> v;
            Q.pb({2, l, r, v, i});
            E[l] = r, D[l] = v;
        }else if(t == 3){
            cin >> l;
            Q.pb({2, l, E[l], -D[l], i});
        }else{
            cin >> l >> r;
            Q.pb({3, l, r, 0, i});
        }
    }
    CDQ(Q);
    for(int i = 0; i <= q; i++){
        if(ans[i]) cout << ans[i] << "\n";
    }
    return 0;
}

拔掉網路線

By thanksone

拔掉網路線

  • 814