经典数据结构问题

静态区间颜色数

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(区间最值操作、区间历史最值)

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

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

在每一次操作后,我们都进行一次更新,让 $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维莫队,块大小取$\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;
}

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

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

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

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

#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]$ 区间内的所有数全变成 $0$;
  • 1 l r 把 $[l, r]$ 区间内的所有数全变成 $1$;
  • 2 l r 把 $[l,r]$ 区间内的所有数全部取反,也就是说把所有的 $0$ 变成 $1$,把所有的 $1$ 变成 $0$;
  • 3 l r 询问 $[l, r]$ 区间内总共有多少个 $1$;
  • 4 l r 询问 $[l, r]$ 区间内最多有多少个连续的 $1$。
#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;
}

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

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

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

// 权值线段树+离散化 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小

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

  • Q l r k 表示查询下标在区间 $[l,r]$ 中的第 $k$ 小的数
  • C x y 表示将 $a_x$ 改为 $y$
// 整体二分+树状数组 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]] << ' ';
  }
}