title: 经典数据结构问题
categories:

  • ICPC
    tags:
  • null
    abbrlink: cecfa4d9
    date: 2023-03-31 00:00:00

经典数据结构问题

静态区间颜色数

void solve() {
    int n;
    cin >> n;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];
    int m;
    cin >> m;
    int mx = *max_element(alls(a));
    vector<vector<pii>> q(n + 1);
    for (int i = 1; i <= m; i++) {
        int l, r;
        cin >> l >> r;
        q[r].push_back({l, i});
    }
    deb("?");
    Fwk<int> fwk(mx + 10);
    vector<int> last(mx + 1);
    auto add = [&](int l, int r, int x) {
        fwk.add(l, x);
        fwk.add(r + 1, -x);
    };
    vector<int> ans(m + 1);
    deb("?");
    for (int r = 1; r <= n; r++) {
        add(last[a[r]] + 1, r, 1);
        last[a[r]] = r;
        for (auto [l, id] : q[r]) {
            ans[id] = fwk.sum(l);
        }
    }
    for (int i = 1; i <= m; i++) cout << ans[i] << endl;
}

静态区间不同数之和

void solve() {
    int n, m;
    cin >> n;
    cin >> m;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];
    // int m;
    // cin >> m;
    int mx = *max_element(alls(a));
    vector<vector<pii>> q(n + 1);
    for (int i = 1; i <= m; i++) {
        int l, r;
        cin >> l >> r;
        q[r].push_back({l, i});
    }
    deb("?");
    Fwk<int> fwk(mx + 10);
    vector<int> last(mx + 1);
    auto add = [&](int l, int r, int x) {
        fwk.add(l, x);
        fwk.add(r + 1, -x);
    };
    vector<int> ans(m + 1);
    deb("?");
    for (int r = 1; r <= n; r++) {
        add(last[a[r]] + 1, r, a[r]);
        last[a[r]] = r;
        for (auto [l, id] : q[r]) {
            ans[id] = fwk.sum(l);
        }
    }
    for (int i = 1; i <= m; i++) cout << ans[i] << endl;
}

静态二维数点:多次询问一个矩形内有多少个点

void solve() {
    vector<int> vy;
    int n, m;
    cin >> n >> m;
    vector<a5> event;
    for (int i = 1; i <= n; i++) {
        int x, y;
        cin >> x >> y;
        vy.push_back(y);
        event.push_back({x, 0, 0, y, 0});
    }
    Fwk<int> fwk(n + 1);
    for (int i = 1; i <= m; i++) {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        event.push_back({x1 - 1, 1, 1, y1 - 1, i});
        event.push_back({x2, 1, 1, y2, i});
        event.push_back({x1 - 1, 1, -1, y2, i});
        event.push_back({x2, 1, -1, y1 - 1, i});
    }
    sort(alls(vy));
    vy.erase(unique(alls(vy)), vy.end());
    sort(alls(event));
    vector<int> ans(m + 1);
    for (auto [x, ty, sgn, y, id] : event) {
        if (ty == 0) {
            int val = lower_bound(alls(vy), y) - vy.begin() + 1;
            assert(val <= n);
            fwk.add(val, 1);
        } else {
            int val = upper_bound(alls(vy), y) - vy.begin() + 1 - 1;
            assert(val <= n);
            ans[id] += sgn * fwk.sum(val);
        }
    }
    for (int i = 1; i <= m; i++) cout << ans[i] << endl;
}

矩形面积并:扫描线维护最小值出现次数

template <class Info, class Tag>
struct LazySegmentTree {
    int n;
    vector<Info> info;
    vector<Tag> tag;
    LazySegmentTree() : n(0) {}
    LazySegmentTree(int n_, Info v_ = Info()) { init(vector<Info>(n_ + 1, v_)); }
    LazySegmentTree(vector<Info> t_) { init(t_); }
    void init(vector<Info> a)  //[1,n]
    {
        n = a.size() - 1;
        info.assign((n << 2) + 1, Info());
        tag.assign((n << 2) + 1, Tag());
        function<void(int, int, int)> build = [&](int x, int l, int r) -> void {
            if (l == r) {
                info[x] = a[l];
                return;
            }
            int mid = (l + r) >> 1;
            build(x << 1, l, mid);
            build(x << 1 | 1, mid + 1, r);
            pushup(x);
        };
        build(1, 1, n);
    }
    void pushup(int x) { info[x] = info[x << 1] + info[x << 1 | 1]; }
    void apply(int p, const Tag& v) {
        info[p].apply(v);  // 标记更新自己
        tag[p].apply(v);   // 下传标记
    }
    void pushdown(int x) {
        apply(x << 1, tag[x]);
        apply(x << 1 | 1, tag[x]);
        tag[x] = Tag();
    }
    void update(int x, int l, int r, int p, const Info& v) {
        if (l == r) {
            info[x] = v;
            return;
        }
        int mid = (l + r) >> 1;
        pushdown(x);
        if (p <= mid)
            update(x << 1, l, mid, p, v);
        else
            update(x << 1 | 1, mid + 1, r, p, v);
        pushup(x);
    }
    void update(int p, const Info& v) { update(1, 1, n, p, v); }
    Info query(int x, int l, int r, int ql, int qr) {
        if (l > qr || r < ql)
            return Info();
        if (ql <= l && r <= qr)
            return info[x];
        int mid = (l + r) >> 1;
        pushdown(x);
        return query(x << 1, l, mid, ql, qr) +
               query(x << 1 | 1, mid + 1, r, ql, qr);
    }
    Info query(int ql, int qr) { return query(1, 1, n, ql, qr); }
    void rangeupdate(int x, int l, int r, int ql, int qr, const Tag& v) {
        if (l > qr || r < ql)
            return;
        if (ql <= l && r <= qr) {
            apply(x, v);
            return;
        }
        int mid = (l + r) >> 1;
        pushdown(x);
        rangeupdate(x << 1, l, mid, ql, qr, v);
        rangeupdate(x << 1 | 1, mid + 1, r, ql, qr, v);
        pushup(x);
    }
    void rangeupdate(int ql, int qr, const Tag& v) {
        rangeupdate(1, 1, n, ql, qr, v);
    }
    template <class F>
    int findFirst(int x, int l, int r, int ql, int qr, F pred) {
        if (l > qr || r < ql || !pred(info[x]))
            return -1;
        if (l == r)
            return l;
        int mid = (l + r) >> 1;
        pushdown(x);
        int res = findFirst(x << 1, l, mid, ql, qr, pred);
        if (res == -1)
            res = findFirst(x << 1 | 1, mid + 1, r, ql, qr, pred);
        return res;
    }
    template <class F>
    int findFirst(int l, int r, F pred) {
        return findFirst(1, 1, n, l, r, pred);
    }
    template <class F>
    int findLast(int x, int l, int r, int ql, int qr, F pred) {
        if (l > qr || r < ql || !pred(info[x]))
            return -1;
        if (l == r)
            return l;
        int mid = (l + r) >> 1;
        pushdown(x);
        int res = findLast(x << 1 | 1, mid + 1, r, ql, qr, pred);
        if (res == -1)
            res = findLast(x << 1, l, mid, ql, qr, pred);
        return res;
    }
    template <class F>
    int findLast(int l, int r, F pred) {
        return findLast(1, 1, n, l, r, pred);
    }
};
struct Tag {
    ll add = 0;
    void apply(const Tag& v) { add += v.add; }  // 标记怎么合并
};
struct Info {
    ll minv = 0, mincnt = 1;
    void apply(const Tag& v) { minv += v.add; }  // 标记怎么更新节点信息
};
Info operator+(const Info& a, const Info& b) {  // 维护的信息怎么合并
    if (a.minv == b.minv)
        return {a.minv, a.mincnt + b.mincnt};
    else if (a.minv < b.minv)
        return a;
    else
        return b;
}
#define a4 array<int, 4>
void solve() {
    int n;
    cin >> n;
    vector<a4> event;
    vector<int> vy;
    for (int i = 1; i <= n; i++) {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        event.push_back({x1, 1, y1, y2});
        event.push_back({x2, -1, y1, y2});
        vy.push_back(y1);
        vy.push_back(y2);
    }
    sort(alls(vy));
    vy.erase(unique(alls(vy)), vy.end());
    vector<Info> tmp(1);
    int nodecnt = 0;
    for (int i = 1; i < (int)vy.size(); i++) {
        tmp.push_back({0, vy[i] - vy[i - 1]});
        //cerr << "tmp" << i << " " << vy[i] - vy[i - 1] << endl;
        nodecnt++;
    }

    LazySegmentTree<Info, Tag> seg(tmp);
    ll ans = 0;
    sort(alls(event));
    int prex = 0;
    ll len = seg.query(1, nodecnt).mincnt;
    for (auto [x, op, y1, y2] : event) {
        int idy1 = lower_bound(alls(vy), y1) - vy.begin() + 1;
        int idy2 = lower_bound(alls(vy), y2) - vy.begin();
        deb(x, op, y1, y2);
        deb(idy1, idy2);
        ll d = len;
        deb(len);
        auto [val, cnt] = seg.query(1, nodecnt);
        if (val == 0)
            d -= cnt;
        deb(prex, x, d);
        ans += (ll)(x - prex) * d;
        prex = x;
        seg.rangeupdate(idy1, idy2, {op});
    }
    cout << ans << endl;
}

静态区间mex:注意线段树的单点修改是赋值操作

struct Tag {
    ll add = 0;
    void apply(const Tag& v) { add += v.add; }  // 标记怎么合并
};
struct Info {
    ll sum = 0, len = 1;
    void apply(const Tag& v) { sum += len * v.add; }  // 标记怎么更新节点信息
};
Info operator+(const Info& a, const Info& b) {  // 维护的信息怎么合并
    Info c = Info();
    c.sum = min(a.sum, b.sum);
    c.len = a.len + b.len;
    return c;
}
void solve() {
    int n, m;
    cin >> n >> m;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];
    vector<vector<pii>> q(m + 1);
    for (int i = 1; i <= m; i++) {
        int l, r;
        cin >> l >> r;
        q[r].push_back({l, i});
    }
    vector<int> ans(m + 1);
    LazySegmentTree<Info, Tag> seg(n + 10);
    ////map<int, int> mp;
    for (int r = 1; r <= n; r++) {
        a[r] = min(a[r], n + 3);
        seg.update(a[r] + 1, {r, 1});
        // mp[a[r] + 1] = r;
        for (auto [l, id] : q[r]) {
            deb(id, l, r);
            ans[id] = seg.findFirst(1, n + 2, [&](Info v) {
                if (v.sum < l)
                    return true;
                return false;
            });
        }
    }
    for (int i = 1; i <= m; i++) cout << ans[i] - 1 << endl;
}

找到每个数都出现k次的区间数量

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int long long
#define R(i, l, r) for (int i = (l); i <= (r); ++i)
const int N = 5e5 + 5;
const ll P = 1e18;
int n, a[N];


__int128 hsh[N];
signed main() {
    int t;
    cin >> t;
    while (t--) {
        int k;
        cin >> n >> k;  
        mt19937_64 rnd(time(0));
        int ans=0;
        map<int,int>rdm;
      
        map<int, int> cnt;
        map<__int128, int> mp;
        for(int i=0;i<=n;i++)hsh[i]=0;
        R(i, 1, n) {
            hsh[i] = hsh[i - 1];
            cin >> a[i];
            if(rdm.count(a[i])==0)rdm[a[i]] = rnd() % P + 1;
            hsh[i] -= 1ll * cnt[a[i]] * rdm[a[i]];
            ++cnt[a[i]];
            cnt[a[i]] %= k;
            hsh[i] += 1ll * cnt[a[i]] * rdm[a[i]];
        }
        mp[0] = 1;
        map<int, int> cnt2;
        for (int i = 1, j = 0; i <= n; ++i) {
            ++cnt2[a[i]];
            while (cnt2[a[i]] > k) --cnt2[a[j]], (j ? --mp[hsh[j - 1]] : 1), ++j;
            ans += mp[hsh[i]];
            ++mp[hsh[i]];
        }
        cout << ans << '\n';
    }
    return 0;
}

线段树维护区间加等差数列

image-20241208032115496

#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define Pair pair<LL, LL>
#define ls rt << 1
#define rs rt << 1 | 1
#define PI acos(-1.0)
#define eps 1e-13
#define mod 998244353
#define MAXN 50001
#define MS 100005

int n, m;
int a[MS];
struct node {
    LL val;
    LL la;
} p[MS << 2];

void push_up(int rt) {
    p[rt].val = p[ls].val + p[rs].val;
}

void push_down(int rt, int l, int r) {
    if (p[rt].la) {
        int m = l + r >> 1;
        p[ls].val += p[rt].la * (m - l + 1);
        p[rs].val += p[rt].la * (r - m);
        p[ls].la += p[rt].la;
        p[rs].la += p[rt].la;
        p[rt].la = 0;
    }
}

void build(int l, int r, int rt) {
    if (l == r) {
        p[rt].val = a[l] - a[l - 1];
        return;
    }
    int m = l + r >> 1;
    build(l, m, ls);
    build(m + 1, r, rs);
    push_up(rt);
}

void modify(int L, int R, int l, int r, int rt, LL val) {
    if (L <= l && r <= R) {
        p[rt].val += val * (r - l + 1);
        p[rt].la += val;
        return;
    }
    int m = l + r >> 1;
    push_down(rt, l, r);
    if (m >= L)
        modify(L, R, l, m, ls, val);
    if (m < R)
        modify(L, R, m + 1, r, rs, val);
    push_up(rt);
}

LL query(int L, int R, int l, int r, int rt) {
    if (L <= l && r <= R) {
        return p[rt].val;
    }
    int m = l + r >> 1;
    push_down(rt, l, r);
    LL ans = 0;
    if (m >= L)
        ans += query(L, R, l, m, ls);
    if (m < R)
        ans += query(L, R, m + 1, r, rs);
    return ans;
}

inline void solve() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> a[i];
    build(1, n, 1);
    while (m--) {
        int op;
        cin >> op;
        if (op == 1) {
            LL l, r, k, d;
            cin >> l >> r >> k >> d;
            modify(l, l, 1, n, 1, k);
            if (l + 1 <= r)
                modify(l + 1, r, 1, n, 1, d);
            if (r + 1 <= n)
                modify(r + 1, r + 1, 1, n, 1, -(d * (r - l) + k));
        } else if (op == 2) {
            int pos;
            cin >> pos;
            cout << query(1, pos, 1, n, 1) << "\n";
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    //	srand((unsigned)time(0));
    //	freopen("data.in","r",stdin);
    //	freopen("data.out","w",stdout);
    int ce = 1;
    //    cin >> ce;
    //    scanf("%d",&ce);
    while (ce--) solve();

    return 0;
}
/*


*/

线段树 3(区间最值操作、区间历史最值)

给出一个长度为 nn 的数列 AA,同时定义一个辅助数组 BBBB 开始与 AA 完全相同。接下来进行了 mm 次操作,操作有五种类型,按以下格式给出:

  • 1 l r k:对于所有的 i[l,r]i\in[l,r],将 AiA_i 加上 kkkk 可以为负数)。
  • 2 l r v:对于所有的 i[l,r]i\in[l,r],将 AiA_i 变成 min(Ai,v)\min(A_i,v)
  • 3 l r:求 i=lrAi\sum_{i=l}^{r}A_i
  • 4 l r:对于所有的 i[l,r]i\in[l,r],求 AiA_i 的最大值。
  • 5 l r:对于所有的 i[l,r]i\in[l,r],求 BiB_i 的最大值。

在每一次操作后,我们都进行一次更新,让 Bimax(Bi,Ai)B_i\gets\max(B_i,A_i)

#include <bits/stdc++.h>

#ifdef LOCAL
#include "debug.h"
#else
#define deb(...) 114514
#endif

using namespace std;

using LL = long long;
using pii = pair<int, int>;
using pll = pair<LL, LL>;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int Mod = 1e9 + 7;
const int N = 3e5 + 10;

class LSGT {
public:
    struct tag {
        LL addmax, addmax_, add, add_;
        tag() : addmax(0), addmax_(-INF), add(0), add_(-INF) {}
        tag(LL add) : addmax(add), addmax_(add), add(add), add_(add) {}
        tag(LL addmax, LL addmax_, LL add, LL add_) : addmax(addmax), addmax_(addmax_), add(add), add_(add_) {}
        bool empty() const {
            return addmax == 0 && addmax_ == -INF && add == 0 && add_ == -INF;
        }
        void apply(const tag &o) {
            add_ = max(add_, add + o.add_);
            addmax_ = max(addmax_, addmax + o.addmax_);
            add += o.add;
            addmax += o.addmax;
        }
    };
    struct info {
        LL cntM1, sum, M1, M2, len, b;
        info() : cntM1(1), sum(0), M1(-INF), M2(-INF), len(1), b(0) {}
        info(LL x) : cntM1(1), sum(x), M1(x), M2(-INF), len(1), b(x) {}
        info(LL cntM1, LL sum, LL M1, LL M2, LL len, LL b) : cntM1(cntM1), sum(sum), M1(M1), M2(M2), len(len), b(b) {}
        friend info operator+(const info &a, const info &b) {
            info res;
            res.len = a.len + b.len;
            res.sum = a.sum + b.sum;
            res.b = max(a.b, b.b);
            if (a.M1 == b.M1) {
                res.cntM1 = a.cntM1 + b.cntM1;
                res.M1 = a.M1;
                res.M2 = max(a.M2, b.M2);
            } else if (a.M1 > b.M1) {
                res.cntM1 = a.cntM1;
                res.M1 = a.M1;
                res.M2 = max(a.M2, b.M1);
            } else {
                res.cntM1 = b.cntM1;
                res.M1 = b.M1;
                res.M2 = max(b.M2, a.M1);
            }
            return res;
        }
        void apply(const tag &o) {
            sum += o.add * len - o.add * cntM1 + o.addmax * cntM1;
            b = max(b, M1 + o.addmax_);
            M1 += o.addmax;
            M2 += o.add;
        }
    };

private:
    std::vector<info> node;
    std::vector<tag> ta;
    int siz;
    void build(int idx, int l, int r) {
        if (l == r)
            return;
        int mid = (l + r) >> 1;
        build(idx << 1, l, mid), build(idx << 1 | 1, mid + 1, r);
        node[idx] = node[idx << 1] + node[idx << 1 | 1];
    }
    template <typename T>
    void build(int idx, int l, int r, const std::vector<T> &vec) {
        if (l == r) {
            node[idx] = vec[l];
            return;
        }
        int mid = (l + r) >> 1;
        build(idx << 1, l, mid, vec), build(idx << 1 | 1, mid + 1, r, vec);
        node[idx] = node[idx << 1] + node[idx << 1 | 1];
    }
    void apply(int idx) {
        if (ta[idx].empty())
            return;
        LL maxx = max(node[idx << 1].M1, node[idx << 1 | 1].M1);
        if (node[idx << 1].M1 == maxx)
            node[idx << 1].apply(ta[idx]), ta[idx << 1].apply(ta[idx]);
        else
            node[idx << 1].apply({ta[idx].add, ta[idx].add_, ta[idx].add, ta[idx].add_}), ta[idx << 1].apply({ta[idx].add, ta[idx].add_, ta[idx].add, ta[idx].add_});
        if (node[idx << 1 | 1].M1 == maxx)
            node[idx << 1 | 1].apply(ta[idx]), ta[idx << 1 | 1].apply(ta[idx]);
        else
            node[idx << 1 | 1].apply({ta[idx].add, ta[idx].add_, ta[idx].add, ta[idx].add_}), ta[idx << 1 | 1].apply({ta[idx].add, ta[idx].add_, ta[idx].add, ta[idx].add_});
        ta[idx] = {};
    }
    void modify(int idx, int l, int r, int ql, int qr, tag add) {
        if (add.add == 0 && add.addmax >= node[idx].M1)
            return;
        if (ql <= l && qr >= r && (add.add != 0 || add.addmax > node[idx].M2)) {
            if (add.add == 0) {
                add.addmax = -node[idx].M1 + add.addmax;
                add.addmax_ = add.addmax;
            }
            ta[idx].apply(add);
            node[idx].apply(add);
            return;
        }
        apply(idx);
        int mid = (l + r) >> 1;
        if (ql <= mid)
            modify(idx << 1, l, mid, ql, qr, add);
        if (qr > mid)
            modify(idx << 1 | 1, mid + 1, r, ql, qr, add);
        node[idx] = node[idx << 1] + node[idx << 1 | 1];
    }
    info query(int idx, int l, int r, int ql, int qr) {
        if (ql <= l && qr >= r)
            return node[idx];
        apply(idx);
        int mid = (l + r) >> 1;
        if (qr <= mid)
            return query(idx << 1, l, mid, ql, qr);
        else if (ql > mid)
            return query(idx << 1 | 1, mid + 1, r, ql, qr);
        else
            return query(idx << 1, l, mid, ql, qr) + query(idx << 1 | 1, mid + 1, r, ql, qr);
    }

public:
    LSGT(const int size) : node(size << 2), ta(size << 2), siz(size) {
        build(1, 1, siz);
    }
    template <typename T>
    LSGT(const std::vector<T> &vec) : node(vec.size() << 2), ta(vec.size() << 2), siz(vec.size() - 1) {
        build(1, 1, siz, vec);
    }
    void modify(int ql, int qr, const tag &add) {
        modify(1, 1, siz, ql, qr, add);
    }
    info query(int ql, int qr) {
        return query(1, 1, siz, ql, qr);
    }
};

void solve() {
    int n, m;
    cin >> n;
    cin >> m;
    vector<int> arr(n + 1);
    for (int i = 1; i <= n; i++) cin >> arr[i];
    LSGT tr(arr);
    while (m--) {
        int op;
        cin >> op;
        int l, r;
        cin >> l >> r;
        if (op == 1) {
            int x;
            cin >> x;
            if (x)
                tr.modify(l, r, x);
        } else if (op == 2) {
            int x;
            cin >> x;
            tr.modify(l, r, {x, 0, 0, 0});
        } else if (op == 3) {
            cout << tr.query(l, r).sum << '\n';
        } else if (op == 4) {
            cout << tr.query(l, r).M1 << '\n';
        } else if (op == 5) {
            cout << tr.query(l, r).b << '\n';
        }
    }
}

signed main() {
#ifdef LOCAL
    clock_t tttttttt = clock();
    freopen("in.txt", "r", stdin);
#endif
#ifndef LOCAL
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
#endif
    //*****************************************************************
    int t = 1;
    // cin >> t;
    while (t--) solve();
//*****************************************************************
#ifdef LOCAL
    cerr << "Time Used: " << fixed << setprecision(3) << (clock() - tttttttt) / (CLOCKS_PER_SEC / 1000.0) << " ms" << endl;
#endif
    return 0;
}

区间限制最大值最小值, 区间加, 查询区间最小值最大值,区间和

class LSGT {
public:
    struct tag {
        LL add, Min, Max;
        tag() : add(0), Min(INF), Max(-INF) {}
        tag(LL add) : add(add), Min(INF), Max(-INF) {}
        tag(LL add, LL Min, LL Max) : add(add), Min(Min), Max(Max) {}
        bool empty() const {
            return add == 0 && Min == INF && Max == -INF;
        }
        void apply(const tag &o) {
            add += o.add;
            if (Min != INF)
                Min += o.add;
            if (Max != -INF)
                Max += o.add;
            if (Min <= o.Max) {
                Min = Max = o.Max;
            } else if (Max >= o.Min) {
                Min = Max = o.Min;
            } else {
                Min = min(o.Min, Min);
                Max = max(Max, o.Max);
            }
        }
    };
    struct info {
        LL cntM1, sum, M1, M2, len, cntm1, m1, m2;
        info() : cntM1(1), sum(0), M1(0), M2(-INF), len(1), cntm1(1), m1(0), m2(INF) {}
        info(LL x) : cntM1(1), sum(x), M1(x), M2(-INF), len(1), cntm1(1), m1(x), m2(INF) {}
        info(LL cntM1, LL sum, LL M1, LL M2, LL len, LL cntm1, LL m1, LL m2) : cntM1(cntM1), sum(sum), M1(M1), M2(M2), len(len), cntm1(cntm1), m1(m1), m2(m2) {}
        friend info operator+(const info &a, const info &b) {
            info res;
            res.len = a.len + b.len;
            res.sum = a.sum + b.sum;
            if (a.M1 == b.M1) {
                res.cntM1 = a.cntM1 + b.cntM1;
                res.M1 = a.M1;
                res.M2 = max(a.M2, b.M2);
            } else if (a.M1 > b.M1) {
                res.cntM1 = a.cntM1;
                res.M1 = a.M1;
                res.M2 = max(a.M2, b.M1);
            } else {
                res.cntM1 = b.cntM1;
                res.M1 = b.M1;
                res.M2 = max(b.M2, a.M1);
            }
            if (a.m1 == b.m1) {
                res.cntm1 = a.cntm1 + b.cntm1;
                res.m1 = a.m1;
                res.m2 = min(a.m2, b.m2);
            } else if (a.m1 < b.m1) {
                res.cntm1 = a.cntm1;
                res.m1 = a.m1;
                res.m2 = min(a.m2, b.m1);
            } else {
                res.cntm1 = b.cntm1;
                res.m1 = b.m1;
                res.m2 = min(a.m1, b.m2);
            }
            return res;
        }
        void apply(const tag &o) {
            sum += o.add * len;
            M1 += o.add;
            M2 += o.add;
            m1 += o.add;
            m2 += o.add;
            if (M1 == m1) {
                if (M1 > o.Min) {
                    sum -= (M1 - o.Min) * cntM1;
                    m1 = M1 = o.Min;
                }
                if (m1 < o.Max) {
                    sum -= (m1 - o.Max) * cntm1;
                    M1 = m1 = o.Max;
                }
                return;
            }
            if (o.Min == o.Max) {
                sum = len * o.Min;
                M1 = m1 = o.Min;
                cntm1 = cntM1 = len;
                return;
            }
            if (M1 > o.Min) {
                sum -= (M1 - o.Min) * cntM1;
                if (M1 == m2)
                    m2 = M1 = o.Min;
                else
                    M1 = o.Min;
            }
            if (m1 < o.Max) {
                sum -= (m1 - o.Max) * cntm1;
                if (m1 == M2)
                    m1 = M2 = o.Max;
                else
                    m1 = o.Max;
            }
        }
    };

private:
    std::vector<info> node;
    std::vector<tag> ta;
    int siz;
    void build(int idx, int l, int r) {
        if (l == r)
            return;
        int mid = (l + r) >> 1;
        build(idx << 1, l, mid), build(idx << 1 | 1, mid + 1, r);
        node[idx] = node[idx << 1] + node[idx << 1 | 1];
    }
    template <typename T>
    void build(int idx, int l, int r, const std::vector<T> &vec) {
        if (l == r) {
            node[idx] = vec[l];
            return;
        }
        int mid = (l + r) >> 1;
        build(idx << 1, l, mid, vec), build(idx << 1 | 1, mid + 1, r, vec);
        node[idx] = node[idx << 1] + node[idx << 1 | 1];
    }
    void apply(int idx) {
        if (ta[idx].empty())
            return;
        ta[idx << 1].apply(ta[idx]);
        ta[idx << 1 | 1].apply(ta[idx]);
        node[idx << 1].apply(ta[idx]);
        node[idx << 1 | 1].apply(ta[idx]);
        ta[idx] = {};
    }
    void modify(int idx, int l, int r, int ql, int qr, const tag &add) {
        if (!add.add && add.Min >= node[idx].M1 && add.Max <= node[idx].m1)
            return;
        if (ql <= l && qr >= r && add.Min > node[idx].M2 && add.Max < node[idx].m2) {
            ta[idx].apply(add);
            node[idx].apply(add);
            return;
        }
        apply(idx);
        int mid = (l + r) >> 1;
        if (ql <= mid)
            modify(idx << 1, l, mid, ql, qr, add);
        if (qr > mid)
            modify(idx << 1 | 1, mid + 1, r, ql, qr, add);
        node[idx] = node[idx << 1] + node[idx << 1 | 1];
    }
    info query(int idx, int l, int r, int ql, int qr) {
        if (ql <= l && qr >= r)
            return node[idx];
        apply(idx);
        int mid = (l + r) >> 1;
        if (qr <= mid)
            return query(idx << 1, l, mid, ql, qr);
        else if (ql > mid)
            return query(idx << 1 | 1, mid + 1, r, ql, qr);
        else
            return query(idx << 1, l, mid, ql, qr) + query(idx << 1 | 1, mid + 1, r, ql, qr);
    }

public:
    LSGT(const int size) : node(size << 2), ta(size << 2), siz(size) {
        build(1, 1, siz);
    }
    template <typename T>
    LSGT(const std::vector<T> &vec) : node(vec.size() << 2), ta(vec.size() << 2), siz(vec.size() - 1) {
        build(1, 1, siz, vec);
    }
    void modify(int ql, int qr, const tag &add) {
        modify(1, 1, siz, ql, qr, add);
    }
    info query(int ql, int qr) {
        return query(1, 1, siz, ql, qr);
    }
};
void solve() {
    int n, m;
    cin >> n;
    vector<int> arr(n + 1);
    for (int i = 1; i <= n; i++) cin >> arr[i];
    LSGT tr(arr);
    cin >> m;
    while (m--) {
        int op;
        cin >> op;
        int l, r;
        cin >> l >> r;
        if (op == 1) {
            int x;
            cin >> x;
            // 区间加 x
            tr.modify(l, r, {x});
        } else if (op == 2) {
            int x;
            cin >> x;
            // 区间取max(x,a[i])
            tr.modify(l, r, {0, INF, x});
        } else if (op == 3) {
            int x;
            cin >> x;
            // 区间取min(x,a[i])
            tr.modify(l, r, {0, x, -INF});
        } else if (op == 4) {
            // 区间求和
            cout << tr.query(l, r).sum << '\n';
        } else if (op == 5) {
            // 区间最大值
            cout << tr.query(l, r).M1 << '\n';
        } else if (op == 6) {
            // 区间最小值
            cout << tr.query(l, r).m1 << '\n';
        }
    }
}

signed main() {
#ifdef LOCAL
    clock_t tttttttt = clock();
    freopen("in.txt", "r", stdin);
#endif
#ifndef LOCAL
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
#endif
    //*****************************************************************
    int t = 1;
    // cin >> t;
    while (t--) solve();
//*****************************************************************
#ifdef LOCAL
    cerr << "Time Used: " << fixed << setprecision(3) << (clock() - tttttttt) / (CLOCKS_PER_SEC / 1000.0) << " ms" << endl;
#endif
    return 0;
}

在线静态区间mex

#include <bits/stdc++.h>

using namespace std;
const int N = 200005;

int tot = 0;
struct node {
    int ls, rs;
    int v;
} seg[N * 24];
int rt[N], a[N];
int n, m;

void pushup(int k)
{
    seg[k].v = min(seg[seg[k].ls].v, seg[seg[k].rs].v);
}
int modify(int old, int l, int r, int pos, int v)
{
    int p = ++tot;
    seg[p] = seg[old];
    if (l == r)
    {
        seg[p].v = v;
        return p;
    }
    int mid = (l + r) >> 1;
    if (pos <= mid)
        seg[p].ls = modify(seg[old].ls, l, mid, pos, v);
    else
        seg[p].rs = modify(seg[old].rs, mid + 1, r, pos, v);
    pushup(p);
    return p;
}
int query(int p, int l, int r, int pos)
{
    if (l == r)
    {
        return l;
    }
    int mid = (l + r) >> 1;
    if (seg[seg[p].ls].v < pos)
        return query(seg[p].ls, l, mid, pos);
    else
        return query(seg[p].rs, mid + 1, r, pos);
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        rt[i] = modify(rt[i - 1], 0, n, a[i], i);
    }
    while (m--)
    {
        int l, r;
        cin >> l >> r;
        cout << query(rt[r], 0, n, l) << '\n';
    }
}

单点修改区间颜色数:容易证明 k维莫队,块大小取nm1k\frac{n}{m^\frac{1}{k}}时时间复杂度最优。

#include <bits/stdc++.h>
using namespace std;
const int N = 133334;
struct Modify {
    int pos, x; // a[pos] -> x
} M[N];
struct Query {
    int l, r, t, idx;
};
char opt;
vector<Query> Q;
int n, m, q, t, l, r, a[N];
int cur, ans[N], cnt[1000010];
inline void optimizeIO(void) {
    ios::sync_with_stdio(false);
    cin.tie(NULL), cout.tie(NULL);
}
inline void init(void) {
    const int B = n / pow(m, 1.0 / 3);
    sort(Q.begin(), Q.end(), [=](Query &x, Query &y) {
        if (x.l / B != y.l / B) return x.l < y.l;
        if (x.r / B != y.r / B) return x.r < y.r;
        return (x.r / B) & 1 ? x.t < y.t : x.t > y.t;
    });
}
inline void add(int val) {
    if (cnt[val]++ == 0) ++cur;
}
inline void del(int val) {
    if (--cnt[val] == 0) --cur;
}
inline void upd(int t, int l, int r) { // 维护由于时间造成的变化
    if (l <= M[t].pos && M[t].pos <= r) {
        del(a[M[t].pos]), add(M[t].x);
    }
    // 这是一个很巧妙的操作。
    // 由于做完这次操作之后,再一次做这个操作一定是相反的操作,
    // 所以可以直接交换当前位置的值和操作更改后的值。
    swap(a[M[t].pos], M[t].x); 
}
inline void MoAlgo(void) {
    int l = 1, r = 0, t = 0; // 当前状态
    for (int i = 0; i < q; i++) {
        while (Q[i].l < l) add(a[--l]);
        while (r < Q[i].r) add(a[++r]);
        while (l < Q[i].l) del(a[l++]);
        while (Q[i].r < r) del(a[r--]);
        while (t < Q[i].t) upd(++t, l, r);
        while (Q[i].t < t) upd(t--, l, r);
        ans[Q[i].idx] = cur;
    }
}
int main(int argc, char const *argv[]) {
    optimizeIO(), cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> a[i];
    // q -> 询问的个数,等于 Q.size()
    // t -> 当前时间戳
    for (int i = 1; i <= m; i++) {
        cin >> opt >> l >> r;
        if (opt == 'R') M[++t] = {l, r};
        else Q.push_back({l, r, t, ++q});
    }
    init(), MoAlgo();
    for (int i = 1; i <= q; i++) {
        cout << ans[i] << '\n';
    }
    return 0;
}

dx实现

// 带修莫队 O(n^(5/3))
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
using namespace std;

const int N = 1000005;
int n, m, B, mq, mr, a[N];
int sum, cnt[N], ans[N];
struct Q {  // 询问
    int l, r, id, tim;
    // 按l/B、r/B和tim排序
    bool operator<(Q &b) {
        if (l / B != b.l / B)
            return l < b.l;
        if (r / B != b.r / B)
            return r < b.r;
        return tim < b.tim;
    }
} q[N];
struct R {  // 替换
    int p, c;
} R[N];

void add(int x) {
    if (!cnt[x])
        sum++;  // x第一次则累计
    cnt[x]++;   // x出现次数
}
void del(int x) {
    cnt[x]--;
    if (!cnt[x])
        sum--;
}
int main() {
    scanf("%d%d", &n, &m);
    B = pow(n, 0.66);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for (int i = 1; i <= m; i++) {  // 操作
        char c[2];
        int l, r;
        scanf("%s%d%d", c, &l, &r);
        if (c[0] == 'Q')
            q[++mq] = {l, r, mq, mr};
        else
            R[++mr] = {l, r};
    }
    sort(q + 1, q + 1 + mq);
    for (int i = 1, l = 1, r = 0, x = 0; i <= mq; i++) {
        while (l > q[i].l) add(a[--l]);  // 左扩展
        while (r < q[i].r) add(a[++r]);  // 右扩展
        while (l < q[i].l) del(a[l++]);  // 左删除
        while (r > q[i].r) del(a[r--]);  // 右删除
        while (x < q[i].tim) {           // 时间戳变大,替换
            int p = R[++x].p;
            // 位置p介于[l,r],先删旧数,后加新数
            if (l <= p && p <= r)
                del(a[p]), add(R[x].c);
            swap(a[p], R[x].c);  // 交换a,R的对应数
        }
        while (x > q[i].tim) {  // 时间戳变小,还原
            int p = R[x].p;
            if (l <= p && p <= r)
                del(a[p]), add(R[x].c);
            swap(a[p], R[x--].c);
        }
        ans[q[i].id] = sum;
    }
    for (int i = 1; i <= mq; i++) printf("%d\n", ans[i]);
}

区间修改区间颜色数

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
using ll = long long;
int n, QT, a[N], qt, rq, pr[N], mp[N], mt;
struct Q {
    int l, r, d, p;
} q[N * 6], q2[N * 6];
struct odt {
    int l, r;
    mutable int d;
    bool operator<(const odt &z)
        const { return l < z.l; }
};
struct rag {
    int l, r;
    bool operator<(const rag &z)
        const { return r < z.r; }
};
void addp(int x, int d) { q[++rq] = {pr[x], x, d}; }
void cgpr(int x, int y) {
    if (pr[x] != y)
        addp(x, -1), pr[x] = y, addp(x, 1);
}
struct RGdt : set<rag> {
    void add(int l, int r) {
        auto it = lower_bound({0, r});
        if (it != end())
            cgpr(it->l, r);
        if (it != begin())
            --it, cgpr(l, it->r);
        else
            cgpr(l, 0);
        insert({l, r});
    }
    void del(int l, int r) {
        erase({l, r});
        auto it = lower_bound({0, r});
        if (it != end()) {
            if (it != begin()) {
                auto at = it;
                --at;
                cgpr(it->l, at->r);
            } else
                cgpr(it->l, 0);
        }
    }
} lt[N];
struct Odt : set<odt> {
    set<odt>::iterator split(int r) {
        if (r > n)
            return end();
        auto it = lower_bound({r});
        if (it != end() && it->l == r)
            return it;
        --it;
        int L = it->l, R = it->r, D = it->d;
        lt[D].erase({L, R}), lt[D].insert({L, r - 1});
        lt[D].insert({r, R});
        erase(it), insert({L, r - 1, D});
        auto res = insert({r, R, D}).first;
        return res;
    }
} ADT;
ll ct[N], ans[N];
void solve(int l, int r) {
    if (l >= r)
        return;
    int i, x, y, md = l + r >> 1;
    solve(l, md), solve(md + 1, r);
    for (x = md + 1, y = l; x <= r; ++x) {
        while (y <= md && q[y].l <= q[x].l) {
            if (!q[y].p)
                for (i = q[y].r; i <= n; i += i & -i) ct[i] += q[y].d;
            ++y;
        }
        if (q[x].p)
            for (i = q[x].r; i; i -= i & -i) ans[q[x].p] += q[x].d * ct[i];
    }
    for (x = l; x < y; ++x)
        if (!q[x].p)
            for (i = q[x].r; i <= n; i += i & -i) ct[i] -= q[x].d;
    merge(q + l, q + md + 1, q + md + 1, q + r + 1, q2 + l, [&](Q x, Q y) {
        return x.l < y.l;
    });
    for (i = l; i <= r; ++i) q[i] = q2[i];
}
int main() {
    ios::sync_with_stdio(false);
    int i, j, A, l, r, d, qi;
    cin >> n >> QT;
    for (i = 1; i <= n; ++i) cin >> a[i], mp[++mt] = a[i];
    for (qi = 1; qi <= QT; ++qi) {
        cin >> q2[qi].p >> q2[qi].l >> q2[qi].r;
        if (q2[qi].p == 1) {
            cin >> q2[qi].d;
            mp[++mt] = q2[qi].d;
        }
    }
    sort(mp + 1, mp + mt + 1), unique(mp + 1, mp + mt + 1) - mp - 1;
    for (i = 1; i <= n; ++i) a[i] = lower_bound(mp + 1, mp + mt + 1, a[i]) - mp;
    for (qi = 1; qi <= QT; ++qi)
        if (q2[qi].p == 1)
            q2[qi].d = lower_bound(mp + 1, mp + mt + 1, q2[qi].d) - mp;
    for (i = 1; i <= n; ++i) {
        ADT.insert({i, i, a[i]});
        if (lt[a[i]].size()) {
            auto it = lt[a[i]].end();
            --it;
            pr[i] = it->r;
        }
        lt[a[i]].insert({i, i});
        addp(i, 1);
    }
    for (qi = 1; qi <= QT; ++qi) {
        A = q2[qi].p, l = q2[qi].l, r = q2[qi].r, d = q2[qi].d;
        if (A == 1) {
            auto R = ADT.split(r + 1), L = ADT.split(l);
            auto it = L;
            lt[it->d].del(it->l, it->r);
            for (++it; it != R; ++it) cgpr(it->l, it->l - 1), lt[it->d].del(it->l, it->r);
            ADT.erase(L, R);
            ADT.insert({l, r, d});
            lt[d].add(l, r);
        } else {
            ++qt, q[++rq] = {l - 1, r, 1, qt};
            q[++rq] = {l - 1, l - 1, -1, qt};
        }
    }
    solve(1, rq);
    for (i = 1; i <= qt; ++i)
        printf("%lld\n", ans[i]);
    return 0;
}

给你一个数组{ai}\{a_i\}支持两种操作:

1、查询区间[l,r][l,r]中每个数字出现次数的mex。

2、单点修改某一个位置的值。

mex指的是一些数字中最小的未出现的自然数。值得注意的是,因为 {ai}\{a_i\} 是正整数,所以 00 的出现次数为 00,即操作 11 的答案不会是 00

#include<bits/stdc++.h>
using namespace std;

struct Query {
    int l, r, t, id;
};

struct Change {
    int pos, v;
};

const int MAXN = 100000 * 2 + 5;

Query q[MAXN];
Change c[MAXN];
int v[MAXN], cnt[MAXN], tot[MAXN], ans[MAXN], a[MAXN];
int cntQ, cntC;
int n, m, t, siz, mex = 1;

bool cmp(Query& a, Query& b) {
    if(a.l / t != b.l / t) {
        return a.l < b.l;
    }
    if(a.r / t != b.r / t) {
        return a.r < b.r;
    }
    return a.t < b.t;
}

void add(int x) {
    tot[cnt[x]]--;
    if(tot[cnt[x]] == 0) {
        mex = min(mex, cnt[x]);
    }
    cnt[x]++;
    tot[cnt[x]]++;
    if(tot[cnt[x]] == 1 && mex == cnt[x]) {
        for(int i = mex; i <= siz; i++) {
            if(tot[i + 1] == 0) {
                mex = i + 1;
                break;
            }
        }
    }
}

void del(int x) {
    tot[cnt[x]]--;
    if(tot[cnt[x]] == 0) {
        mex = min(mex, cnt[x]);
    }
    cnt[x]--;
    tot[cnt[x]]++;
    if(tot[cnt[x]] == 1 && mex == cnt[x]) {
        for(int i = mex; i <= siz; i++) {
            if(tot[i + 1] == 0) {
                mex = i + 1;
                break;
            }
        }
    }
}

void modify(int i, int t) {
    if(q[i].l <= c[t].pos && c[t].pos <= q[i].r) {
        del(a[c[t].pos]);
        add(c[t].v);
    }
    swap(a[c[t].pos], c[t].v);
}

int main() {
    scanf("%d%d", &n, &m);
    t = pow(n, 2.0 / 3);
    for(int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        v[++siz] = a[i];
    }
    for(int i = 1; i <= m; i++) {
        int op;

        scanf("%d", &op);
        if(op == 1) {
            cntQ++;
            scanf("%d%d", &q[cntQ].l, &q[cntQ].r);
            q[cntQ].t = cntC;
            q[cntQ].id = cntQ;
        } else {
            cntC++;
            scanf("%d%d", &c[cntC].pos, &c[cntC].v);
            v[++siz] = c[cntC].v;
        }
    }
    sort(v + 1, v + siz + 1);
    siz = unique(v + 1, v + siz + 1) - (v + 1);
    for(int i = 1; i <= n; i++) {
        a[i] = lower_bound(v + 1, v + siz + 1, a[i]) - v;
    }
    for(int i = 1; i <= cntC; i++) {
        c[i].v = lower_bound(v + 1, v + siz + 1, c[i].v) - v;
    }
    sort(q + 1, q + cntQ + 1, cmp);
    int l = 1, r = 0, t = 0;
    for(int i = 1; i <= cntQ; i++) {
        while(l > q[i].l) add(a[--l]);
        while(r < q[i].r) add(a[++r]);
        while(l < q[i].l) del(a[l++]);
        while(r > q[i].r) del(a[r--]);
        while(t < q[i].t) modify(i, ++t);
        while(t > q[i].t) modify(i, t--);
        ans[q[i].id] = mex;
    }
    for(int i = 1; i <= cntQ; i++) {
        printf("%d\n", ans[i]);
    }

    return 0;
}

莫队:小z的袜子(区间内选到相同颜色的数的概率)

// 普通莫队 O(n*sqrt(n))
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
using namespace std;

const int N = 50005;
int n, m, B, a[N];
int sum, cnt[N], ans1[N], ans2[N];
struct Q {
    int l, r, id;
    // 按l所在块的编号l/B和r排序
    bool operator<(const Q &x) const {
        if (l / B != x.l / B)
            return l < x.l;
        if ((l / B) & 1)
            return r < x.r;
        return r > x.r;
    }
} q[N];

void add(int x) {
    sum += cnt[x];  // 当前x与前面每个x组合成双
    cnt[x]++;       // x的出现次数
}
void del(int x) {
    cnt[x]--;
    sum -= cnt[x];
}
int gcd(int a, int b) {
    return b ? gcd(b, a % b) : a;
}
int main() {
    scanf("%d%d", &n, &m);
    B = sqrt(n);                  // 块的大小
    for (int i = 1; i <= n; i++)  // 颜色
        scanf("%d", &a[i]);
    for (int i = 1; i <= m; i++)  // 询问
        scanf("%d%d", &q[i].l, &q[i].r), q[i].id = i;
    sort(q + 1, q + m + 1);
    for (int i = 1, l = 1, r = 0; i <= m; i++) {
        if (q[i].l == q[i].r) {
            ans1[q[i].id] = 0, ans2[q[i].id] = 1;
            continue;
        }
        while (l > q[i].l) l--, add(a[l]);  // 左扩展
        while (r < q[i].r) r++, add(a[r]);  // 右扩展
        while (l < q[i].l) del(a[l]), l++;  // 左删除
        while (r > q[i].r) del(a[r]), r--;  // 右删除
        ans1[q[i].id] = sum;
        ans2[q[i].id] = 1ll * (r - l + 1) * (r - l) / 2;
    }
    for (int i = 1; i <= m; i++) {
        if (ans1[i]) {
            int g = gcd(ans1[i], ans2[i]);
            ans1[i] /= g, ans2[i] /= g;  // 最简分数
        } else
            ans2[i] = 1;
        printf("%d/%d\n", ans1[i], ans2[i]);
    }
}

李超线段树

// 李超线段树 O(nlognlogn)
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;

const double eps = 1e-9;
const int N = 100005;
#define M1 39989
#define M2 1000000000
#define ls u << 1
#define rs u << 1 | 1
typedef pair<double, int> pdi;
int n, cnt, lastans;
struct line {
    double k, b;  // 斜率,截距
} p[N];
int tr[N << 2];  // 线段编号

int cmp(double a, double b) {  // 比a,b
    if (a - b > eps)
        return 1;
    if (b - a > eps)
        return -1;
    return 0;
}
double Y(int id, int x) {  // 求Y值
    return p[id].k * x + p[id].b;
}
void change(int u, int l, int r, int L, int R, int id) {  // 修改
    int mid = (l + r) >> 1;
    if (L <= l && r <= R) {
        int cm = cmp(Y(id, mid), Y(tr[u], mid));
        if (cm == 1 || (!cm && id < tr[u]))
            swap(id, tr[u]);
        int cl = cmp(Y(id, l), Y(tr[u], l));
        if (cl == 1 || (!cl && id < tr[u]))
            change(ls, l, mid, L, R, id);
        int cr = cmp(Y(id, r), Y(tr[u], r));
        if (cr == 1 || (!cr && id < tr[u]))
            change(rs, mid + 1, r, L, R, id);
        return;
    }
    if (L <= mid)
        change(ls, l, mid, L, R, id);
    if (mid < R)
        change(rs, mid + 1, r, L, R, id);
}
pdi pmax(pdi a, pdi b) {  // Y值大且编号小
    if (cmp(a.first, b.first) == 1)
        return a;
    else if (cmp(a.first, b.first) == -1)
        return b;
    else
        return a.second < b.second ? a : b;
}
pdi query(int u, int l, int r, int x) {  // 查询
    if (l == r)
        return {Y(tr[u], x), tr[u]};
    int mid = (l + r) >> 1;
    pdi t = {Y(tr[u], x), tr[u]};
    if (x <= mid)
        return pmax(t, query(ls, l, mid, x));
    else
        return pmax(t, query(rs, mid + 1, r, x));
}
int main() {
    int op, x0, y0, x1, y1, x;
    double k, b;
    scanf("%d", &n);
    while (n--) {
        scanf("%d", &op);
        if (op == 1) {  // 插入一条线段
            scanf("%d%d%d%d", &x0, &y0, &x1, &y1);
            x0 = (x0 + lastans - 1) % M1 + 1,
            x1 = (x1 + lastans - 1) % M1 + 1;
            y0 = (y0 + lastans - 1) % M2 + 1,
            y1 = (y1 + lastans - 1) % M2 + 1;
            if (x0 > x1)
                swap(x0, x1), swap(y0, y1);
            if (x0 == x1)
                k = 0, b = max(y0, y1);
            else
                k = 1.0 * (y1 - y0) / (x1 - x0), b = y0 - k * x0;
            p[++cnt] = {k, b};
            change(1, 1, M1, x0, x1, cnt);
        } else {
            scanf("%d", &x);  // 查询最大值
            x = (x + lastans - 1) % M1 + 1;
            lastans = query(1, 1, M1, x).second;
            printf("%d\n", lastans);
        }
    }
}

img

李超树dp

#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;

#define ll long long
#define ls u << 1
#define rs u << 1 | 1
const int N = 100005, M = 1000005;

ll h[N], s[N], k[N], b[N], f[N];
int n, tr[M << 2];  // 优势线段的编号

ll Y(int id, int x) {  // 求Y值
    return k[id] * x + b[id];
}
void change(int u, int l, int r, int id) {  // 修改
    int mid = l + r >> 1;
    if (Y(id, mid) < Y(tr[u], mid))
        swap(id, tr[u]);
    if (Y(id, l) < Y(tr[u], l))
        change(ls, l, mid, id);
    if (Y(id, r) < Y(tr[u], r))
        change(rs, mid + 1, r, id);
}
ll query(int u, int l, int r, int x) {  // 查询
    if (l == r)
        return Y(tr[u], x);
    int mid = l + r >> 1;
    ll ans = Y(tr[u], x);
    if (x <= mid)
        return min(ans, query(ls, l, mid, x));
    else
        return min(ans, query(rs, mid + 1, r, x));
}
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) scanf("%lld", h + i);
    for (int i = 1; i <= n; ++i) scanf("%lld", s + i), s[i] += s[i - 1];
    k[0] = 0, b[0] = 1e18;  // 第0条线段
    k[1] = -2 * h[1];
    b[1] = h[1] * h[1] - s[1];
    change(1, 1, M, 1);  // 插入第一条线段
    for (int i = 2; i <= n; ++i) {
        f[i] = h[i] * h[i] + s[i - 1] + query(1, 1, M, h[i]);
        k[i] = -2 * h[i];
        b[i] = f[i] + h[i] * h[i] - s[i];
        change(1, 1, M, i);
    }
    printf("%lld", f[n]);
}

普通斜率优化

img

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long LL;
const int N = 500010;
int n,m,q[N];
LL s[N],f[N];

LL dy(int i,int j){return f[i]+s[i]*s[i]-f[j]-s[j]*s[j];}
LL dx(int i,int j){return s[i]-s[j];}
int main(){
  while(~scanf("%d%d",&n,&m)){
    for(int i=1;i<=n;i++)scanf("%lld",&s[i]),s[i]+=s[i-1];

    int h=1,t=0;
    for(int i=1;i<=n;i++){
      while(h<t && dy(i-1,q[t])*dx(q[t],q[t-1])
                 <=dx(i-1,q[t])*dy(q[t],q[t-1])) t--;
      q[++t]=i-1;      
      while(h<t && dy(q[h+1],q[h])
                 <=dx(q[h+1],q[h])*2*s[i]) h++;
      int j=q[h];
      f[i]=f[j]+(s[i]-s[j])*(s[i]-s[j])+m;
    }
    printf("%lld\n",f[n]);
  }
}

单点修改,权值线段树合并

struct SegmentTree {
#define mid ((l + r) >> 1)
    struct TREE {
        int l, r;
        int cnt;
    } tr[N * 20];  /// 2nlogn
    int tot;
    void push_up(int cur) {
        tr[cur].cnt = tr[tr[cur].l].cnt + tr[tr[cur].r].cnt;
    }
    void change(int &cur, int l, int r, int pos, int x) {
        if (!cur)
            cur = ++tot;
        if (l == r) {
            tr[cur].cnt = 1;
            return;
        }
        if (pos <= mid)
            change(tr[cur].l, l, mid, pos, x);
        else
            change(tr[cur].r, mid + 1, r, pos, x);
        push_up(cur);
    }
    int merge(int a, int b, int l, int r) {
        if (!a || !b)
            return a | b;
        if (l == r)
            return tr[a].cnt ? a : b;
        int cur = ++tot;
        tr[cur].l = merge(tr[a].l, tr[b].l, l, mid);
        tr[cur].r = merge(tr[a].r, tr[b].r, mid + 1, r);
        push_up(cur);
        return cur;
    }
    int query(int cur, int l, int r, int ql, int qr) {
        if (!cur || ql < l || qr > r || ql > qr)
            return 0;
        if (l == ql && r == qr)
            return tr[cur].cnt;
        if (qr <= mid)
            return query(tr[cur].l, l, mid, ql, qr);
        else if (ql > mid)
            return query(tr[cur].r, mid + 1, r, ql, qr);
        return query(tr[cur].l, l, mid, ql, mid) + query(tr[cur].r, mid + 1, r, mid + 1, qr);
    }
} seg;

楼房重建

img

// 线段树+递归合并 nlognlogn
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;

const int N = 100005;
#define ls u << 1
#define rs u << 1 | 1
#define mid ((l + r) >> 1)
double mx[N << 2];
int sum[N << 2];
// mx:区间最大斜率, sum:区间可见楼房数

int dfs(int u, int l, int r, double mls) {  // 求右分支sum
    if (mx[u] <= mls)
        return 0;  // 剪枝
    if (l == r)
        return mx[u] > mls;  // 叶子
    if (mx[ls] <= mls)
        return dfs(rs, mid + 1, r, mls);
    else
        return dfs(ls, l, mid, mls) + sum[u] - sum[ls];
}
void pushup(int u, int l, int r) {  // 上传
    mx[u] = max(mx[ls], mx[rs]);
    sum[u] = sum[ls] + dfs(rs, mid + 1, r, mx[ls]);
}
void change(int u, int l, int r, int x, double v) {  // 点修
    if (l == r) {
        mx[u] = v;
        sum[u] = 1;
        return;
    }
    if (x <= mid)
        change(ls, l, mid, x, v);
    else
        change(rs, mid + 1, r, x, v);
    pushup(u, l, r);
}
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        change(1, 1, n, x, (double)y / x);
        printf("%d\n", sum[1]);
    }
}

势能线段树:区间开方

#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
using namespace std;

const int N = 100005;
#define LL long long
#define ls u << 1
#define rs u << 1 | 1
#define mid ((l + r) >> 1)
LL a[N];
LL sum[N << 2], mx[N << 2];
// sum:区间和,mx:区间最大

void pushup(int u) {  // 上传
    sum[u] = sum[ls] + sum[rs];
    mx[u] = max(mx[ls], mx[rs]);
}
void build(int u, int l, int r) {  // 建树
    sum[u] = mx[u] = a[l];
    if (l == r)
        return;
    build(ls, l, mid);
    build(rs, mid + 1, r);
    pushup(u);
}
void change(int u, int l, int r, int x, int y) {  // 区修
    if (mx[u] == 1)
        return;  // 优化
    if (l == r) {
        sum[u] = sqrt(sum[u]);
        mx[u] = sqrt(mx[u]);
        return;
    }
    if (x <= mid)
        change(ls, l, mid, x, y);
    if (y > mid)
        change(rs, mid + 1, r, x, y);
    pushup(u);
}
LL query(int u, int l, int r, int x, int y) {  // 区查
    if (x <= l && r <= y)
        return sum[u];
    LL s = 0;
    if (x <= mid)
        s += query(ls, l, mid, x, y);
    if (y > mid)
        s += query(rs, mid + 1, r, x, y);
    return s;
}
int main() {
    int n, m, opt, l, r;
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
    build(1, 1, n);
    scanf("%d", &m);
    while (m--) {
        scanf("%d%d%d", &opt, &l, &r);
        if (l > r)
            swap(l, r);
        if (opt == 0)
            change(1, 1, n, l, r);
        else
            printf("%lld\n", query(1, 1, n, l, r));
    }
    return 0;
}

给出一个动态的区间,以及m*m次询问,每次询问给出一段区间 [l,r],要求出这段区间中最大连续子段和

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

#define ls u << 1
#define rs u << 1 | 1
const int N = 500010;
int n, m, a[N];
struct Tree {  // 线段树
    int l, r;
    // 区间和,最大左子段和,最大右子段和,最大子段和
    int sum, lmx, rmx, mx;
} tr[N * 4];

void pushup(Tree &u, Tree l, Tree r) {
    u.sum = l.sum + r.sum;
    u.lmx = max(l.lmx, l.sum + r.lmx);
    u.rmx = max(r.rmx, r.sum + l.rmx);
    u.mx = max(max(l.mx, r.mx), l.rmx + r.lmx);
}
void build(int u, int l, int r) {  // 建树
    tr[u] = {l, r, a[r], a[r], a[r], a[r]};
    if (l == r)
        return;
    int m = l + r >> 1;
    build(ls, l, m);
    build(rs, m + 1, r);
    pushup(tr[u], tr[ls], tr[rs]);
}
void change(int u, int x, int v) {  // 点修
    if (tr[u].l == tr[u].r) {
        tr[u] = {x, x, v, v, v, v};
        return;
    }
    int m = tr[u].l + tr[u].r >> 1;
    if (x <= m)
        change(ls, x, v);
    else
        change(rs, x, v);
    pushup(tr[u], tr[ls], tr[rs]);
}
Tree query(int u, int x, int y) {  // 区查
    if (x <= tr[u].l && tr[u].r <= y)
        return tr[u];
    int m = tr[u].l + tr[u].r >> 1;
    if (y <= m)
        return query(ls, x, y);
    if (x > m)
        return query(rs, x, y);
    Tree T;  // 开一个临时节点,存储拼凑结果
    pushup(T, query(ls, x, m), query(rs, m + 1, y));
    return T;
}
// Tree query(int u,int x,int y){ //区查
//   if(x>tr[u].r||y<tr[u].l)
//     return {tr[u].l,tr[u].r,0,-1e9,-1e9,-1e9};
//   if(x<=tr[u].l && tr[u].r<=y) return tr[u];
//   Tree T; //开一个临时节点,存储拼凑结果
//   pushup(T,query(ls,x,y),query(rs,x,y));
//   return T;
// }
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    build(1, 1, n);
    while (m--) {
        int k, x, y;
        scanf("%d%d%d", &k, &x, &y);
        if (k == 1) {
            if (x > y)
                swap(x, y);
            printf("%d\n", query(1, x, y).mx);
        } else
            change(1, x, y);
    }
    return 0;
}
  • 0 l r[l,r][l, r] 区间内的所有数全变成 00
  • 1 l r[l,r][l, r] 区间内的所有数全变成 11
  • 2 l r[l,r][l,r] 区间内的所有数全部取反,也就是说把所有的 00 变成 11,把所有的 11 变成 00
  • 3 l r 询问 [l,r][l, r] 区间内总共有多少个 11
  • 4 l r 询问 [l,r][l, r] 区间内最多有多少个连续的 11
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;

#define ls u << 1
#define rs u << 1 | 1
const int N = 100005;
int n, m, a[N];
struct tree {
    int l, r;
    int b, lb, rb, mb, c, lc, rc, mc;
    int len, tag, rev;
} tr[N << 2];
// b:区间1的个数,      c:区间0的个数
// lb:区间左起1的长度, lc:区间左起0的长度
// rb:区间右起1的长度, rc:区间右起0的长度
// mb:区间1的最长长度, mc:区间0的最长长度
// len:区间的长度
// tag:区间赋值标记,无标记:-1,有标记:0或1
// rev:区间取反标记,无标记: 0,有标记:1

void pushup(tree& u, tree l, tree r) {  // 上传
    u.b = l.b + r.b;
    u.lb = l.c ? l.lb : l.b + r.lb;
    u.rb = r.c ? r.rb : r.b + l.rb;
    u.mb = max(max(l.mb, r.mb), l.rb + r.lb);
    u.c = l.c + r.c;
    u.lc = l.b ? l.lc : l.c + r.lc;
    u.rc = r.b ? r.rc : r.c + l.rc;
    u.mc = max(max(l.mc, r.mc), l.rc + r.lc);
}
void pd(int u, int opt) {  // 操作区间
    tree& t = tr[u];
    if (opt == 0) {  // 区间赋值为0
        t.b = t.lb = t.rb = t.mb = 0;
        t.c = t.lc = t.rc = t.mc = t.len;
        t.tag = 0;
        t.rev = 0;
    }
    if (opt == 1) {  // 区间赋值为1
        t.b = t.lb = t.rb = t.mb = t.len;
        t.c = t.lc = t.rc = t.mc = 0;
        t.tag = 1;
        t.rev = 0;
    }
    if (opt == 2) {  // 区间取反
        swap(t.b, t.c);
        swap(t.lb, t.lc);
        swap(t.rb, t.rc);
        swap(t.mb, t.mc);
        t.rev ^= 1;
    }
}
void pushdown(int u) {  // 下传
    tree& t = tr[u];
    if (t.tag == 0)
        pd(ls, 0), pd(rs, 0);
    if (t.tag == 1)
        pd(ls, 1), pd(rs, 1);
    if (t.rev)
        pd(ls, 2), pd(rs, 2);
    t.tag = -1;
    t.rev = 0;
}
void build(int u, int l, int r) {  // 建树
    int t = a[l];
    tr[u] = {l, r, t, t, t, t,
             t ^ 1, t ^ 1, t ^ 1, t ^ 1, r - l + 1, -1, 0};
    if (l == r)
        return;
    int m = l + r >> 1;
    build(ls, l, m);
    build(rs, m + 1, r);
    pushup(tr[u], tr[ls], tr[rs]);
}
void change(int u, int x, int y, int k) {  // 区修
    if (y < tr[u].l || tr[u].r < x)
        return;
    if (x <= tr[u].l && tr[u].r <= y) {
        pd(u, k);
        return;
    }
    pushdown(u);
    change(ls, x, y, k);
    change(rs, x, y, k);
    pushup(tr[u], tr[ls], tr[rs]);
}
tree query(int u, int x, int y) {  // 区查
    if (x > tr[u].r || y < tr[u].l)
        return {};
    if (x <= tr[u].l && tr[u].r <= y)
        return tr[u];
    pushdown(u);
    tree T;  // 开一个临时节点,存储拼凑结果
    pushup(T, query(ls, x, y), query(rs, x, y));
    return T;
}
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d", a + i);
    build(1, 1, n);

    for (int i = 1; i <= m; i++) {
        int opt, l, r;
        scanf("%d%d%d", &opt, &l, &r);
        ++l, ++r;
        if (opt < 3)
            change(1, l, r, opt);
        else {
            tree t = query(1, l, r);
            printf("%d\n", opt == 3 ? t.b : t.mb);
        }
    }
    return 0;
}

静态区间第k小

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

#define N 200005
#define lc(x) tr[x].l
#define rc(x) tr[x].r
struct node{
  int l,r,s; //s:节点值域中有多少个数
}tr[N*20];
int root[N],idx;
int n,m,a[N];
vector<int> b;

void build(int &x,int l,int r){
  x=++idx;
  if(l==r) return;
  int m=l+r>>1;
  build(lc(x),l,m);
  build(rc(x),m+1,r);
}
void insert(int x,int &y,int l,int r,int k){
  y=++idx; tr[y]=tr[x]; tr[y].s++;
  if(l==r) return;
  int m=l+r>>1;
  if(k<=m) insert(lc(x),lc(y),l,m,k);
  else insert(rc(x),rc(y),m+1,r,k);
}
int query(int x,int y,int l,int r,int k){
  if(l==r) return l;
  int m=l+r>>1;
  int s=tr[lc(y)].s-tr[lc(x)].s;
  if(k<=s) return query(lc(x),lc(y),l,m,k);
  else return query(rc(x),rc(y),m+1,r,k-s);
}
int main(){
  scanf("%d%d",&n,&m);
  for(int i=1; i<=n; i++){
    scanf("%d",&a[i]); b.push_back(a[i]);
  }
  sort(b.begin(),b.end());
  b.erase(unique(b.begin(),b.end()),b.end());
  int bn=b.size();

  build(root[0],1,bn);
  for(int i=1; i<=n; i++){
    int id=lower_bound(b.begin(),b.end(),a[i])-b.begin()+1;
    insert(root[i-1],root[i],1,bn,id);
  }
  while(m--){
    int l,r,k; scanf("%d%d%d",&l,&r,&k);
    int id=query(root[l-1],root[r],1,bn,k)-1;
    printf("%d\n",b[id]);
  }
  return 0;
}

权值线段树+离散化实现平衡树,需要动态地维护一个可重集合 MM,并且提供以下操作:

  1. MM 中插入一个数 xx
  2. MM 中删除一个数 xx(若有多个相同的数,应只删除一个)。
  3. 查询 MM 中有多少个数比 xx 小,并且将得到的答案加一。
  4. 查询如果将 MM 从小到大排列后,排名位于第 xx 位的数。
  5. 查询 MMxx 的前驱(前驱定义为小于 xx,且最大的数)。
  6. 查询 MMxx 的后继(后继定义为大于 xx,且最小的数)。

对于操作 3,5,6,不保证当前可重集中存在数 xx

// 权值线段树+离散化 nlogn
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;

const int N = 100005;
#define ls u << 1
#define rs u << 1 | 1
#define mid ((l + r) >> 1)
int n, m, id;
int opt[N], a[N], b[N];
// opt:操作序号 a:数x b:备份数组
int sum[N << 2];  // 区间数的出现次数之和

void pushup(int u) {  // 上传
    sum[u] = sum[ls] + sum[rs];
}
void change(int u, int l, int r, int x, int k) {  // 插删即点修
    if (l == r) {
        sum[u] += k;
        return;
    }
    if (x <= mid)
        change(ls, l, mid, x, k);
    else
        change(rs, mid + 1, r, x, k);
    pushup(u);
}
int q_rank(int u, int l, int r, int x, int y) {  // 排名即前缀和
    if (x <= l && r <= y)
        return sum[u];
    int s = 0;
    if (x <= mid)
        s += q_rank(ls, l, mid, x, y);
    if (y > mid)
        s += q_rank(rs, mid + 1, r, x, y);
    return s;
}
int q_num(int u, int l, int r, int x) {  // 排名x的数
    if (l == r)
        return l;
    if (x <= sum[ls])
        return q_num(ls, l, mid, x);
    else
        return q_num(rs, mid + 1, r, x - sum[ls]);
}
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d%d", &opt[i], &a[i]);
        if (opt[i] != 4)
            b[++m] = a[i];  // 备份
    }
    sort(b + 1, b + m + 1);                // 排序
    m = unique(b + 1, b + 1 + m) - b - 1;  // 去重
    for (int i = 1; i <= n; i++) {
        if (opt[i] != 4)
            id = lower_bound(b + 1, b + m + 1, a[i]) - b;
        if (opt[i] == 1)
            change(1, 1, m, id, 1);  // 插入x
        if (opt[i] == 2)
            change(1, 1, m, id, -1);  // 删除x
        if (opt[i] == 3)              // x的排名
            printf("%d\n", id > 1 ? q_rank(1, 1, m, 1, id - 1) + 1 : 1);
        if (opt[i] == 4)  // 排名为x的数
            printf("%d\n", b[q_num(1, 1, m, a[i])]);
        if (opt[i] == 5) {  // x的前驱
            int rk = q_rank(1, 1, m, 1, id - 1);
            printf("%d\n", b[q_num(1, 1, m, rk)]);
        }
        if (opt[i] == 6) {  // x的后继
            int rk = q_rank(1, 1, m, 1, id) + 1;
            printf("%d\n", b[q_num(1, 1, m, rk)]);
        }
    }
}

动态区间第k小

给定一个含有 nn 个数的序列 a1,a2ana_1,a_2 \dots a_n,需要支持两种操作:

  • Q l r k 表示查询下标在区间 [l,r][l,r] 中的第 kk 小的数
  • C x y 表示将 axa_x 改为 yy
// 整体二分+树状数组 O(nlognlogV)
#include <algorithm>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;

const int N = 300005;
#define lowbit(x) (x & -x)
int n, m, a[N], ans[N];

struct Q {
    // 查询: [x,y]第k小,id编号,opt=1
    // 原数: x位置,y值,opt=0
    int x, y, k, id, opt;
};
vector<Q> q;  // 数据序列

struct BIT {
    vector<int> s;
    void init(int n) { s.resize(n); }
    void add(int x, int k) {
        while (x <= n) s[x] += k, x += lowbit(x);
    }
    int sum(int x) {
        int t = 0;
        while (x) t += s[x], x -= lowbit(x);
        return t;
    }
    int sum(int l, int r) {
        return sum(r) - sum(l - 1);
    }
} tree;  // 树状数组

void solve(vector<Q> q, int L, int R) {
    if (!q.size())
        return;
    if (L == R) {
        for (auto i : q)
            if (i.opt)
                ans[i.id] = L;
        return;
    }
    int mid = L + R >> 1;
    vector<Q> q1, q2;
    for (auto i : q) {
        if (!i.opt) {  // 若是原数,按值分流
            if (i.y <= mid)
                tree.add(i.x, i.k),   // 加入贡献
                    q1.push_back(i);  // 分流到左边
            else
                q2.push_back(i);  // 分流到右边
        } else {                  // 若是查询,按个数分流
            int s = tree.sum(i.x, i.y);
            if (s >= i.k)
                q1.push_back(i);  // 分流到左边
            else
                i.k -= s, q2.push_back(i);  // 分流到右边
        }
    }
    for (auto i : q1)
        if (!i.opt)
            tree.add(i.x, -i.k);  // 减去贡献
    solve(q1, L, mid);
    solve(q2, mid + 1, R);  // 分治
}
int main() {
    scanf("%d%d", &n, &m);
    char ch[2];
    int x, y, k;
    tree.init(n + 1);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]), q.push_back({i, a[i], 1, 0, 0});
    for (int i = 1; i <= m; i++) {
        scanf("%s%d%d", ch, &x, &y);
        if (ch[0] == 'C')
            q.push_back({x, a[x], -1, 0, 0}),         // 旧数贡献-1
                q.push_back({x, a[x] = y, 1, 0, 0});  // 新数贡献+1
        else
            scanf("%d", &k), q.push_back({x, y, k, i, 1});
    }
    memset(ans, -1, sizeof ans);
    solve(q, 0, 1e9);  // 整体二分
    for (int i = 1; i <= m; i++)
        if (~ans[i])
            printf("%d\n", ans[i]);
}

线段树优化建图

const int MAXN = 5e5 + 5;
const int D = 5e5;
const int INF = 0x3f3f3f3f3f3f3f3fll;
int n, m, s;
int id[MAXN];
int dis[MAXN << 1];
vector<pair<int, int>> g[MAXN << 1];
void build(int x, int l, int r) {
  if (l == r) {
    id[l] = x;
    return;
  }
  int mid = l + r >> 1;
  g[x].push_back({x << 1, 0});
  g[x].push_back({x << 1 | 1, 0});
  g[(x << 1) + D].push_back({x + D, 0});
  g[(x << 1 | 1) + D].push_back({x + D, 0});
  build(x << 1, l, mid);
  build(x << 1 | 1, mid + 1, r);
}
void add(int x, int l, int r, int L, int R, int v, int w, int op) {
  if (L <= l && r <= R) {
    if (op)
      g[v].push_back({x, w});
    else
      g[x + D].push_back({v, w});
    return;
  }
  int mid = l + r >> 1;
  if (L <= mid) add(x << 1, l, mid, L, R, v, w, op);
  if (R > mid) add(x << 1 | 1, mid + 1, r, L, R, v, w, op);
}
void dijkstra(int s) {
  vector<int> vis(MAXN * 2, 0);
  priority_queue<pair<int, int>> q;
  memset(dis, INF, sizeof(dis));
  dis[s] = 0;
  q.push({0, s});
  while (!q.empty()) {
    int x = q.top().second;
    q.pop();
    if (vis[x]) continue;
    vis[x] = 1;
    for (auto [y, w] : g[x]) {
      dis[y] = min(dis[y], dis[x] + w);
      q.push({-dis[y], y});
    }
  }
}
// op 1 : v->u 单向边
// op 2 : v->[l,r]
// op 3 :[l,r]-> v
void solve() {
  cin >> n >> m >> s;
  build(1, 1, n);
  for (int i = 1; i <= n; i++) {
    int x = id[i];
    g[x].push_back({x + D, 0});
    g[x + D].push_back({x, 0});
  }
  while (m--) {
    int op;
    cin >> op;
    if (op == 1) {
      int v, u, w;
      cin >> v >> u >> w;
      g[id[v]].push_back({id[u], w});
    } else if (op == 2) {
      int v, l, r, w;
      cin >> v >> l >> r >> w;
      add(1, 1, n, l, r, id[v], w, 1);
    } else {
      int v, l, r, w;
      cin >> v >> l >> r >> w;
      add(1, 1, n, l, r, id[v], w, 0);
    }
  }
  dijkstra(id[s]);
  for (int i = 1; i <= n; i++) {
    if (dis[id[i]] == INF)
      cout << "-1 ";
    else
      cout << dis[id[i]] << ' ';
  }
}