板板板板板

数据结构

单调队列

// 要求的是每连续的 k 个数中的最大(最小)值,很明显,当一个数进入所要 "寻找" 最大值的范围中时,若这个数比其前面(先进队)的数要大,
//显然,前面的数会比这个数先出队且不再可能是最大值。
#include <bits/stdc++.h>
using namespace std;

int main() 
{
    int n, k;
    cin >> n >> k;
    vector<int> v(n + 1);
    for (int i = 1; i <= n; i++) cin >> v[i];
    deque<int> q1, q2;
    for (int i = 1; i <= k; i++) {
        while (!q1.empty() && v[i] >= v[q1.back()]) q1.pop_back();
        while (!q2.empty() && v[i] <= v[q2.back()]) q2.pop_back();
        q1.push_back(i);
        q2.push_back(i);
    }
    vector<pair<int, int>> res;
    //res[1].first = v[q1.front()], res[1].second = v[q2.front()];
    res.push_back({v[q1.front()], v[q2.front()]});
    for (int i = k + 1; i <= n; i++) {
        while (!q1.empty() && v[i] >= v[q1.back()]) q1.pop_back();
        while (!q2.empty() && v[i] <= v[q2.back()]) q2.pop_back();
        q1.push_back(i);
        q2.push_back(i);
        while (q1.front() <= i - k) q1.pop_front();
        while (q2.front() <= i - k) q2.pop_front();
        res.push_back({v[q1.front()], v[q2.front()]});
    }
    for (int i = 0; i <= n - k; i++) cout << res[i].second << " ";
    cout << endl;
    for (int i = 0; i <= n - k; i++) cout << res[i].first << " ";
    cout << endl;
}

单调栈

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

int main(){
    ios::sync_with_stdio(false); \
    cin.tie(0), cout.tie(0);
    int n;
    cin >> n;
    vector<int> v(n + 1, 0), sta, res(n + 1);
    for (int i = 1; i <= n; i++) cin >> v[i];
    for (int i = 1; i <= n; i++) {
        while (!sta.empty() && v[sta.back()] < v[i]) res[sta.back()] = i, sta.pop_back();
        sta.push_back(i);
    }
    for (int i = 1; i <= n; i++) cout << res[i] << " ";
}

DSU

#include<bits/stdc++.h>
//
struct DSU {
    int fa[maxn];

    void init(int n) {
        for(int i = 0;i <= n; ++i){
            fa[i] = i;
        }
    }

    int find(int x) {
        if(x != fa[x]) fa[x] = find(fa[x]);
        return fa[x];
    }

    bool same(int i, int j) {
        return find(i) == find(j);
    }

    void merge(int i, int j) {
        fa[find(i)] = find(j);
    }

} dsu;


//____________________________________________________________________________________
// 2.带权并查集


struct DSU {
    vector<int> fa;
    vector<int> w;

    DSU(int n) : fa(n + 1), w(n + 1, 0) {
        for (int i = 0; i <= n; i++) {
            fa[i] = i;
        }
    }

    int find(int x) {
        if (x != fa[x]) {
            int root = find(fa[x]);
           // w[x] = (w[x] + w[fa[x]]) % h;
            fa[x] = root;
        }
        return fa[x];
    }

    bool same(int i, int j) {
        return find(i) == find(j);
    }

    bool merge(int x, int y, int v) {
        int px = find(x), py = find(y);
        if (px == py) return false;
    //    w[px] = (v + w[y] - w[x]) % h;
    //    if (w[px] < 0) w[px] += h;
        fa[px] = py;
        return true;
    }
};


// _________________________________________________________________
// 3. 构建指定节点i到j的最小生成树
// 但每次merge的边权值v要大于存在边, 
// 模拟lca的重构数, weight查找两点间最大值
struct DSU {
    vector<int> fa, w;

    DSU(int n) {
        fa.resize(n, -1);
        w.resize(n, 1e9);
    }

    int find(int x) {
        return fa[x] < 0 ? x : find(fa[x]);
    }

    bool same(int i, int j) {
        return find(i) == find(j);
    }

    bool merge(int x, int y, int v) {
        x = find(x), y = find(y);
        if (x == y) return false;
        if (fa[x] > fa[y]) swap(x, y); // 模拟路径压缩
        fa[x] += fa[y], fa[y] = x;
        w[y] = v;
        return true;
    }

    int weight(int i, int j) {
        int tmp = 0;
        while (i != j) {
            if (w[i] < w[j]) tmp = w[i], i = fa[i];
            else tmp = w[j], j = fa[j];
        }
        return tmp;
    }
};


//___________________________________________________________________________
// 可撤销并查集

struct DSU
{
    int fa[maxn];
    int sz[maxn];
    vector<pair<int&, int>>his_sz;
    vector<pair<int&, int>>his_fa;
    void init(int n) {
        for (int i = 1; i <= n; i++)fa[i] = i, sz[i] = 1;
    }
    int find(int x) {
        while (x != fa[x])x = fa[x];
        return x;
    }
    bool same(int u, int v) {
        return find(u) == find(v);
    }
    void merge(int u, int v) {
        int x = find(u);
        int y = find(v);
        if (x == y) return;
        if (sz[x] < sz[y]) std::swap(x, y);
        his_sz.push_back({ sz[x], sz[x] });
        sz[x] = sz[x] + sz[y];
        his_fa.push_back({ fa[y],fa[y] });
        fa[y] = x;
    }
 
    int histroy() {
        return his_fa.size();
    }
 
    void roll(int h) {
        while (his_fa.size() > h) {
            his_fa.back().first = his_fa.back().second;
            his_fa.pop_back();
            his_sz.back().first = his_sz.back().second;
            his_sz.pop_back();
        }
    }
    
}dsu;

ST表

#include <iostream>
using namespace std;
const int maxn = 2e5 + 10;
int a[maxn], f[maxn][__lg(maxn) + 1];

void init(int n) {
    for (int i = 1; i <= n; i++) f[i][0] = a[i];
    for (int j = 1; j <= __lg(n); j++) {
        for (int i = 1; i + (1 << j) - 1 <= n; i++) {
            f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
        }
    }
}

int query(int l, int r) {
    int s = __lg(r - l + 1);
    return max(f[l][s], f[r - (1 << s) + 1][s]);
}

int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    init(n);
    int m;
    cin >> m;
    while (m--) {
        int l, r;
        cin >> l >> r;
        cout << query(l, r) << endl;
    }
}

Trie字典树

// ACW 256
#include <iostream>
#include <algorithm>
using namespace std;

const int maxn = 6e5 + 10, maxm = maxn * 25;

int n, m;
int s[maxn], tr[maxm][2], max_id[maxm], root[maxn], idx;

void insert(int i, int k, int p, int q) {
    if (k < 0) {
        max_id[q] = i;
        return;
    }
    
    int val = s[i] >> k & 1;
    if (p) tr[q][val ^ 1] = tr[p][val ^ 1];
    tr[q][val] = ++idx;

    insert(i, k - 1, tr[p][val], tr[q][val]);

    max_id[q] = max(max_id[tr[q][0]], max_id[tr[q][1]]);
}

void insert(int i) {
    int p = root[i];
    int last = 0;
    if (i) last = root[i - 1];
    for (int j = 23; j >= 0; j--) {
        int val = s[i] >> j & 1;
        tr[p][val] = ++idx;
        if (last) {
            tr[p][val ^ 1] = tr[last][val ^ 1];
            last = tr[last][val];
        }
        max_id[idx] = i;
        p = tr[p][val];
    }
    max_id[p] = i;
}

int query(int l, int r, int num) {
    int p = root[r];
    for (int i = 23; i >= 0; i--) {
        int val = num >> i & 1;
        if (max_id[tr[p][val ^ 1]] >= l) p = tr[p][val ^ 1];
        else p = tr[p][val];
    }
    return num ^ s[max_id[p]];
}

int main() {
    cin >> n >> m;
    root[0] = ++idx;
    max_id[0] = -1;
    insert(0, 23, 0, root[0]);
    for (int i = 1; i <= n; i++) {
        int x;
        cin >> x;
        s[i] = s[i - 1] ^ x;
        root[i] = ++idx;
        insert(i, 23, root[i - 1], root[i]);
    }
    
    while (m--) {
        char op;
        int l, r, x;
        cin >> op;
        if (op == 'A') {
            cin >> x;
            n++;
            s[n] = s[n - 1] ^ x;
            root[n] = ++idx;
            insert(n, 23, root[n - 1], root[n]);
        }
        else {
            cin >> l >> r >> x;
            cout << query(l - 1, r - 1, s[n] ^ x) << endl;
        }
    }
    return 0;
}

LCA

// 在询问的[l, r]中找p使a[p] ^ ... ^ a[n]最大
#include <iostream>
#include <cstring>
using namespace std;

const int maxn = 4e4 + 10, maxm = maxn * 2;
int h[maxn], e[maxm], ne[maxm], idx;
int dep[maxn], fa[maxn][16], q[maxn];

void add(int a, int b) {
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}

void bfs(int root) {
    memset(dep, 0x3f, sizeof(dep));
    dep[0] = 0, dep[root] = 1;
    int hh = 0, tt = 0;
    q[0] = root;
    while (hh <= tt) {
        int t = q[hh++];
        for (int i = h[t]; ~i; i = ne[i]) {
            int j = e[i];
            if (dep[j] > dep[t] + 1) {
                dep[j] = dep[t] + 1;
                q[++tt] = j;
                fa[j][0] = t;
                for (int k = 1; k <= 15; k++) {
                    fa[j][k] = fa[fa[j][k - 1]][k - 1];
                }
            }
        }
    }
}

int lca(int a, int b) {
    if (dep[a] < dep[b]) swap(a, b);
    for (int k = 15; k >= 0; k--) {
        if (dep[fa[a][k]] >= dep[b]) {
            a = fa[a][k];
        }
    }
    if (a == b) return a;
    for (int k = 15; k >= 0; k--) {
        if (fa[a][k] != fa[b][k]) {
            a = fa[a][k];
            b = fa[b][k];
        }
    }
    return fa[a][0];
}

int main() {
    int n;
    cin >> n;
    memset(h, -1, sizeof(h));
    
    int root;
    for (int i = 1; i <= n; i++) {
        int a, b;
        cin >> a >> b;
        if (b == -1) root = a;
        else add(a, b), add(b, a);
    }
    
    bfs(root);

    int m;
    cin >> m;
    while (m--) {
        int a, b;
        cin >> a >> b;
        int p = lca(a, b);
        if (p == a) cout << 1 << endl;
        else if (p == b) cout << 2 << endl;
        else cout << 0 << endl;
    }
    return 0;
}

// 或者

vector<int> g[maxn];
ll dep[maxn], fa[maxn], siz[maxn], son[maxn], top[maxn], res[maxn];

void dfs1(int u, int f) {
    dep[u] = dep[f] + 1;
    fa[u] = f;
    siz[u] = 1;
    for (auto j : g[u]) {
        if (j == f) continue;
        dfs1(j, u);
        siz[u] += siz[j];
        if (siz[j] > siz[son[u]]) son[u] = j;
    }
}

void dfs2(int u, int t) {
    top[u] = t;
    if (!son[u]) return;
    dfs2(son[u], t);
    for (auto j : g[u]) {
        if (j == fa[u] || j == son[u]) continue;
        dfs2(j, j);
    }
}

int lca(int x, int y) {
    while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        x = fa[top[x]];
    }
    return dep[x] < dep[y] ? x : y;
}

树状数组

// 支持单点修改和区间查询, 时间复杂度均为O(logn)
#include <bits/stdc++.h>
#define lowbit(x) ((x) & (-x))
using namespace std;

const int maxn = 5e5 + 5;
int tr[maxn];

inline void update(int i, int x)
{
    for (int pos = i; pos < maxn; pos += lowbit(pos))
    {
        tr[pos] += x;
    }
}

inline int query(int n)
{
    int res = 0;
     for (int pos = n; pos; pos -= lowbit(pos))
    {                                              
        res += tr[pos];
    }
    return res;
}

inline int query(int a, int b)
{
    return query(b) - query(a - 1);
}

线段树

//它主要用于维护区间信息(要求满足结合律)。
//与树状数组相比,它可以实现 O(log n) 的区间修改,还可以同时支持多种操作

//线段树是一棵平衡二叉树。母结点代表整个区间的和,越往下区间越小。注意,线段树的每个节点都对应一条线段(区间)

#include <iostream>
using ll = long long;
using namespace std;

const int maxn = 2e5 + 5;

struct Node {
    int l, r;
    ll sum;
    //int maxv;
    ll lazy;
}tr[maxn * 4];

ll a[maxn];

void pushup(int u) {
    tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
    //tr[u].maxv = max(tr[u << 1].maxv, tr[u << 1 | 1].maxv);
}

void update(int u, ll val) {
    tr[u].sum += (tr[u].r - tr[u].l + 1) * val;
    // tr[u].maxv += val;
    tr[u].lazy += val;
}

void pushdown(int u) {
    if (tr[u].lazy) {
        update(u << 1, tr[u].lazy), update(u << 1 | 1, tr[u].lazy);
        tr[u].lazy = 0;
    }
}

void build(int u, int l, int r)  {
    tr[u].l = l, tr[u].r = r, tr[u].lazy = 0;
    if (l == r) {
        tr[u].sum = a[l];
        // tr[u].maxv = a[l];
        return;
    }
    int mid = l + r >> 1;
    build(u << 1, l, mid);
    build(u << 1 | 1, mid + 1, r);
    pushup(u);
}

void modify(int u, int l, int r, ll val) {
    if (tr[u].l >= l && tr[u].r <= r) {
        update(u, val);
        return;
    }
    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    if (l <= mid) modify(u << 1, l, r, val);
    if (r > mid) modify(u << 1 | 1, l, r, val);
    pushup(u);
}

ll querysum(int u, int l, int r) {
    if (tr[u].l >= l && tr[u].r <= r) {
        return tr[u].sum;
    }
    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    // ll res = 0;
    // if (l <= mid) res += querysum(u << 1, l, r);
    // if (r > mid) res += querysum(u << 1 | 1, l, r);
    // return res;

    if (r <= mid) return querysum(u << 1, l, r);
    else if (l > mid) return querysum(u << 1 | 1, l, r);
    else {
        auto left = querysum(u << 1, l, r);
        auto right = querysum(u << 1 | 1, l, r);
        return left + right;
    }
}

/*
    int querymax(int u, int l, int r)
{
    if (tr[u].l >= l && tr[u].r <= r) return tr[u].maxv;
    int mid = tr[u].l + tr[u].r >> 1;
    int v = 0;
    if (l <= mid) v = querymax(u << 1, l, r);
    if (r > mid) v = max(v, querymax(u << 1 | 1, l, r));
    return v;
 }
*/

// 结构体
struct Segment_Tree {
    void build(int u, int l, int r) {
        tr[u].l = l, tr[u].r = r;
        if (l == r) {
            tr[u].sum = a[l];
            // tr[u].maxv = a[l];
            return;
        }
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
        pushup(u);
    }

    void pushup(int u) {
        tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
        //tr[u].maxv = max(tr[u << 1].maxv, tr[u << 1 | 1].maxv);
    }

    void update(int u, ll val) {
        tr[u].sum += (tr[u].r - tr[u].l + 1) * val;
        // tr[u].maxv += val;
        tr[u].lazy += val;
    }

    void pushdown(int u) {
        if (tr[u].lazy) {
            update(u << 1, tr[u].lazy), update(u << 1 | 1, tr[u].lazy);
            tr[u].lazy = 0;
        }
    }

    void modify(int u, int l, int r, ll val) {
        if (tr[u].l >= l && tr[u].r <= r) {
            update(u, val);
            return;
        }
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;
        if (l <= mid) modify(u << 1, l, r, val);
        if (r > mid) modify(u << 1 | 1, l, r, val);
        pushup(u);
    }

    ll querysum(int u, int l, int r) {
        if (tr[u].l >= l && tr[u].r <= r) {
            return tr[u].sum;
        }
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;
        // ll res = 0;
        // if (l <= mid) res += querysum(u << 1, l, r);
        // if (r > mid) res += querysum(u << 1 | 1, l, r);
        // return res;

        if (r <= mid) return query(u << 1, l, r);
        else if (l > mid) return query(u << 1 | 1, l, r);
        else {
            auto left = query(u << 1, l, r);
            auto right = query(u << 1 | 1, l, r);
            return left + right;
        }
    }

    struct Node {
        int l, r;
        ll sum;
        //int maxv;
        ll lazy;
    }tr[maxn * 4];

}t1, t2; // 记得要build

// 最大字段和
struct Node {
    int l, r;
    ll sum;
    ll rval;
    ll lval;
    ll fval;
}tr[maxn * 4];

void pushup(Node &u, Node &l, Node &r) {
    u.sum = l.sum + r.sum;
    u.lval = max(l.lval, l.sum + r.lval);
    u.rval = max(r.rval, r.sum + l.rval);
    u.fval = max(l.rval + r.lval, max(l.fval, r.fval));
}

void pushup(int u) {
    pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}

void build(int u, int l, int r) {
    tr[u].l = l, tr[u].r = r;
    if (l == r) {
        tr[u].sum = tr[u].lval = tr[u].rval = tr[u].fval = a[l];
        return;
    }
    int mid = l + r >> 1;
    build(u << 1, l, mid);
    build(u << 1 | 1, mid + 1, r);
    pushup(u);
}

void modify(int u, int x, int v) {
    if (tr[u].l == x && tr[u].r == x) {
        tr[u] = {x, x, v, v, v, v};
        return;
    }
    int mid = tr[u].l + tr[u].r >> 1;
    if (x <= mid) modify(u << 1, x, v);
    else modify(u << 1 | 1, x, v);
    pushup(u);
}

Node query(int u, int l, int r) {
    if (l <= tr[u].l && r >= tr[u].r) return tr[u];

    int mid = tr[u].l + tr[u].r >> 1;
    if (r <= mid) return query(u << 1, l, r);
    else if (l > mid) return query(u << 1 | 1, l, r);
    else {
        auto left = query(u << 1, l, r);
        auto right = query(u << 1 | 1, l, r);
        Node res;
        pushup(res, left, right);
        return res;
    }
}

int main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> a[i];
    build(1, 1, n);
    while (m--) {
        int op;
        cin >> op;
        if (op == 1) {
            int x, y;
            ll val;
            cin >> x >> y >> val;
            modify(1, x, y, val);
        }
        else {
            int x, y;
            cin >> x >> y;
            cout << querysum(1, x, y) << endl;
        }
    }
}

主席树

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

int n, m;
const int maxn = 1e5 + 10;
int a[maxn], root[maxn], idx;
vector<int> nums;

int find(int x) {
    return lower_bound(nums.begin(), nums.end(), x) - nums.begin();
}

struct Node {
    int l, r;
    int cnt;
}tr[maxn * 4 + maxn * 17];

int build(int l, int r) {
    int p = idx++;
    if (l == r) {
        return p;
    }
    int mid = l + r >> 1;
    tr[p].l = build(l, mid);
    tr[p].r = build(mid + 1, r);
    return p;
}

int insert(int p, int l, int r, int x) {
    int q = idx++;
    tr[q] = tr[p];
    if (l == r) {
        tr[q].cnt++;
        return q;
    }

    int mid = l + r >> 1;
    if (x <= mid) tr[q].l = insert(tr[p].l, l, mid, x);
    else tr[q].r = insert(tr[p].r, mid + 1, r, x);
    tr[q].cnt = tr[tr[q].l].cnt + tr[tr[q].r].cnt;
    return q;
}

int query(int q, int p, int l, int r, int k) {
    if (l == r) return l;

    int cnt = tr[tr[q].l].cnt - tr[tr[p].l].cnt;
    int mid = l + r >> 1;
    if (k <= cnt) return query(tr[q].l, tr[p].l, l, mid, k);
    else return query(tr[q].r, tr[p].r, mid + 1, r, k - cnt);
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        nums.push_back(a[i]);
    }
    sort(nums.begin(), nums.end());
    nums.erase(unique(nums.begin(), nums.end()), nums.end());
    
    root[0] = build(0, nums.size() - 1);
    for (int i = 1; i <= n; i++) {
        root[i] = insert(root[i - 1], 0, nums.size() - 1, find(a[i]));
    }
    while (m--) {
        int l, r, k;
        cin >> l >> r >> k;
        cout << nums[query(root[r], root[l - 1], 0, nums.size() - 1, k)];
    }
    return 0;
}

分块

#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
using ll = long long;

const int maxn = 1e5 + 10, maxm = 350;

int n, m, len;
ll add[maxm], sum[maxm];
int w[maxn];

int get(int i) {
    return i / len;
}

void change(int l, int r, int d) {
    if (get(l) == get(r)) {
        for (int i = l; i <= r; i++) {
            w[i] += d;
            sum[get(i)] += d;
        }
    }
    else {
        int i = l, j = r;
        while (get(i) == get(l)) w[i] += d, sum[get(i++)] += d;
        while (get(j) == get(r)) w[j] += d, sum[get(j--)] += d;
        for (int k = get(i); k <= get(j); k++) sum[k] += len * d, add[k] += d;
    }
}

ll query(int l, int r) {
    ll res = 0;
    if (get(l) == get(r)) {
        for (int i = l; i <= r; i++) res += w[i] + add[get(i)];
    }
    else {
        int i = l, j = r;
        while (get(i) == get(l)) res += w[i] + add[get(i++)];
        while (get(j) == get(r)) res += w[j] + add[get(j--)];
        for (int k = get(i); k <= get(j); k++) res += sum[k];
    }
    return res;
}

int main() {
    cin >> n >> m;
    len = sqrt(n);
    for (int i = 1; i <= n; i++) {
        cin >> w[i];
        sum[get(i)] += w[i];
    }
    while (m--) {
        char op;
        int l, r;
        cin >> op >> l >> r;
        if (op == 'C') {
            int d;
            cin >> d;
            change(l, r, d);
        }
        else cout << query(l, r) << endl;
    }
    return 0;
}

莫队

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

const int maxn = 5e4 + 10, maxm = 2e5 + 10, S = 1e6 + 10;
int n, m, len;
int w[maxn], res[maxm];

struct Query {
    int id, l, r;
}q[maxm];

int get(int x) {
    return x / len;
}

int cnt[S];

void add(int x, int &res) {
    if (!cnt[x]) res++;
    cnt[x]++;
}

void del(int x, int &res) {
    cnt[x]--;
    if (!cnt[x]) res--;
}

bool cmp(const Query& a, const Query& b) {
    int i = get(a.l), j = get(b.l);
    if (i != j) return i < j;
    return a.r < b.r;
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> w[i];
    cin >> m;
    len = max(1, (int)sqrt((double)n * n / m));
    for (int i = 0; i < m; i++) {
        int l, r;
        cin >> l >> r;
        q[i] = {i, l, r};
    }
    sort(q, q + m, cmp);

    for (int k = 0, i = 0, j = 1, ans = 0; k < m; k++) {
        int id = q[k].id, l = q[k].l, r = q[k].r;
        while (i < r) add(w[++i], ans);
        while (i > r) del(w[i--], ans); 
        while (j < l) del(w[j++], ans);
        while (j > l) add(w[--j], ans); 
        res[id] = ans;
    }

    for (int i = 0; i < m; i++) cout << res[i] << endl;
    return 0;
}

带修莫队

// CF 1476 G
#include <bits /stdc++.h>
using namespace std;
#define endl '\n'
using ll  = long long;
using ULL = unsigned long long;
using PII = pair<int, int>;
using PLI = pair<ll, int>;

const int maxn = 1e5 + 10;
const int maxm = 1e6 + 10; 
const ll  INF = 1e18;
const int mod = 1e9 + 7;
const double eps = 1e-8;
int n, m, k, q;
int a[maxn], ta[maxn], ans[maxn], cnt[maxn], T[maxn];

struct Query {
    int l, r, t, k, id;
}b[maxn];

struct XG {
    int pos, x, y;
}d[maxn];

int L[maxn], R[maxn];

void link_del(int u) {
    int x = L[u], y = R[u];
    R[x] = y, L[y] = x;
}

void link_add(int x, int u) {
    int y = R[x];
    R[x] = u, L[u] = x;
    R[u] = y, L[y] = u;
}

void add(int x) {
    if (T[cnt[x] + 1] == 0) link_add(cnt[x], cnt[x] + 1);
    if (T[cnt[x]] == 1) link_del(cnt[x]);
    T[cnt[x]]--;
    cnt[x]++;
    T[cnt[x]]++;
}

void del(int x) {
    if (T[cnt[x] - 1] == 0) link_add(L[cnt[x]], cnt[x] - 1);
    if (T[cnt[x]] == 1) link_del(cnt[x]);
    T[cnt[x]]--;
    cnt[x]--;
    T[cnt[x]]++;
}

void change(int pos, int x, int l, int r) {
    if (l <= pos && pos <= r) {
        del(a[pos]);
        a[pos] = x;
        add(a[pos]);
    }
    else {
        a[pos] = x;
    }
}

void ToTheMaximum() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        ta[i] = a[i];
    }
    int tot1 = 0, tot2 = 0;
    for (int i = 1; i <= m; i++) {
        int op, x, y, k;
        cin >> op >> x >> y;
        if (op == 1) {
            cin >> k;
            ++tot1;
            b[tot1] = {x, y, tot2, k, tot1};
        } 
        else {
            ++tot2;
            d[tot2] = {x, ta[x], y};
            ta[x] = y;
        }
    }
    int block = pow(n, 2.0 / 3);
    if (block == 0) block = 1;
    sort(b + 1, b + tot1 + 1, [&](Query a, Query b) {
        int k1 = (a.l - 1) / block;
        int k2 = (b.l - 1) / block;
        int k3 = (a.r - 1) / block;
        int k4 = (b.r - 1) / block;
        if (k1 != k2) return k1 < k2;
        if (k3 != k4) return k3 < k4;
        return a.t < b.t;
    });

    R[0] = n + 1, L[n + 1] = 0;
    T[0] = 1e9;
    for (int i = 1, l = 1, r = 0, t = 0; i <= tot1; i++) {
        while (l > b[i].l) add(a[--l]);
        while (r < b[i].r) add(a[++r]);
        while (l < b[i].l) del(a[l++]);
        while (r > b[i].r) del(a[r--]);
        while (t < b[i].t) t++, change(d[t].pos, d[t].y, l, r);
        while (t > b[i].t) change(d[t].pos, d[t].x, l, r), t--;

        int &res = ans[b[i].id];
        res = 1e9;
        int k = b[i].k, p = R[0];
        for (int u = R[0]; u != n + 1; u = R[u]) {
            while (k > 0 && p != n + 1) {
                k -= T[p];
                p = R[p];
            }
            if (k > 0) break;
            res = min(res, L[p] - u);
            k += T[u];
        }
        if (res == 1e9) res = -1;
    } 

    for (int i = 1; i <= tot1; i++) cout << ans[i] << endl;
    cout << endl;
}
  
int main() {
#ifdef LOCAL
    freopen("in.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int w_ = 1;
    //cin >> w_;
    while (w_--) {
        ToTheMaximum();
    }
    return 0;
}

Treap

tree + heap

解决问题: 求排名问题

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

const int maxn = 1e5 + 10, INF = 1e9;
int n, root, idx;

struct Node {
    int l, r;
    int key, val;
    int cnt, size;
}tr[maxn];

void pushup(int u) {
    tr[u].size = tr[u].cnt + tr[tr[u].l].size + tr[tr[u].r].size;
}

int getNode(int key) {
    tr[++idx].key = key;
    tr[idx].val = rand();
    tr[idx].cnt = tr[idx].size = 1;
    return idx;
}

void zig(int &p) { // 右旋
    int q = tr[p].l;
    tr[p].l = tr[q].r, tr[q].r = p, p = q;
    pushup(tr[p].r), pushup(p);
}

void zag(int &p) { // 左旋
    int q = tr[p].r;
    tr[p].r = tr[q].l, tr[q].l = p, p = q;
    pushup(tr[p].l), pushup(p);
} 


void build() {
    getNode(-INF), getNode(INF);
    root = 1, tr[root].r = 2;
    pushup(root);
    
    if (tr[1].val < tr[2].val) zag(root);
}

void insert(int &p, int key) {
    if (!p) p = getNode(key);
    else if (tr[p].key == key) tr[p].cnt++;
    else if (tr[p].key > key) {
        insert(tr[p].l, key);
        if (tr[tr[p].l].val > tr[p].val) zig(p);
    }
    else {
        insert(tr[p].r, key);
        if (tr[tr[p].r].val > tr[p].val) zag(p);
    }
    pushup(p);
}

void remove(int &p, int key) {
    if (!p) return;
    else if (tr[p].key == key) {
        if (tr[p].cnt > 1) tr[p].cnt--;
        else if (tr[p].l || tr[p].r) {
            // 右子树空或左子树val > 右子树val 右旋
            if (!tr[p].r || tr[tr[p].l].val > tr[tr[p].r].val) {
                zig(p);
                remove(tr[p].r, key);
            }
            else {
                zag(p);
                remove(tr[p].l, key);
            }
        }
        else p = 0;
    }
    else if (tr[p].key > key) remove(tr[p].l, key);
    else remove(tr[p].r, key);
    pushup(p);
}

int getRank(int p, int key) {
    if (!p) return 0;
    if (tr[p].key == key) return tr[tr[p].l].size + 1;
    if (tr[p].key > key) return getRank(tr[p].l, key);
    return tr[tr[p].l].size + tr[p].cnt + getRank(tr[p].r, key);
}

int getKey(int p, int rank) {
    if (!p) return INF;
    if (tr[tr[p].l].size >= rank) return getKey(tr[p].l, rank);
    if (tr[tr[p].l].size + tr[p].cnt >= rank) return tr[p].key;
    return getKey(tr[p].r, rank - tr[tr[p].l].size - tr[p].cnt);
}

int getPrev(int p, int key) {
    if (!p) return -INF;
    if (tr[p].key >= key) return getPrev(tr[p].l, key);
    return max(tr[p].key, getPrev(tr[p].r, key));
}

int getNext(int p, int key) {
    if (!p) return INF;
    if (tr[p].key <= key) return getNext(tr[p].r, key);
    return min(tr[p].key, getNext(tr[p].l, key));
}


int main() {
    build();

    cin >> n;
    while (n--) {
        int op, x;
        cin >> op >> x;
        if (op == 1) insert(root, x);
        else if (op == 2) remove(root, x);
        else if (op == 3) cout << getRank(root, x) - 1 << endl;
        else if (op == 4) cout << getKey(root, x + 1) << endl;
        else if (op == 5) cout << getPrev(root, x) << endl;
        else cout << getNext(root, x) << endl;
    }
    return 0;
}

Splay

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

const int maxn = 1e5 + 10;
int n, m, root, idx;

struct Node {
    int s[2], p, v;
    int size, flag;

    void init(int v_, int p_) {
        v = v_;
        p = p_;
        size = 1;
    }

}tr[maxn];

void pushup(int x) {
    tr[x].size = tr[tr[x].s[0]].size + tr[tr[x].s[1]].size + 1;
}

void pushdown(int x) {
    if (tr[x].flag) {
        swap(tr[x].s[0], tr[x].s[1]);
        tr[tr[x].s[0]].flag ^= 1;
        tr[tr[x].s[1]].flag ^= 1;
        tr[x].flag = 0;
    }
}

void rotate(int x) {
    int y = tr[x].p, z = tr[y].p;
    int k = tr[y].s[1] == x; // 0 左儿子 1 右儿子
    tr[z].s[tr[z].s[1] == y] = x, tr[x].p = z;
    tr[y].s[k] = tr[x].s[k ^ 1], tr[tr[x].s[k ^ 1]].p = y;
    tr[x].s[k ^ 1] = y, tr[y].p = x;
    pushup(y), pushup(x);
}

void splay(int x, int k) {
    while (tr[x].p != k) {
        int y = tr[x].p, z = tr[y].p;
        if (z != k) {
            if ((tr[y].s[1] == x) ^ (tr[z].s[1] == y)) rotate(x);
            else rotate(y);
        }
        rotate(x);
    }
    if (!k) root = x;
}

void insert(int v) {
    int u = root;
    int p = 0;
    while (u) p = u, u = tr[u].s[v > tr[u].v];
    u = ++idx;
    if (p) tr[p].s[v > tr[p].v] = u;
    tr[u].init(v, p);
    splay(u, 0);
}

int get_k(int k) {
    int u = root;
    while (true) {
        pushdown(u);
        if (tr[tr[u].s[0]].size >= k) u = tr[u].s[0];
        else if (tr[tr[u].s[0]].size + 1 == k) return u;
        else k -= tr[tr[u].s[0]].size + 1, u = tr[u].s[1];
    }
    return -1;
}

void output(int u) {
    pushdown(u);
    if (tr[u].s[0]) output(tr[u].s[0]);
    if (tr[u].v >= 1 && tr[u].v <= n) cout << tr[u].v << " ";
    if (tr[u].s[1]) output(tr[u].s[1]);
}

int main() {
    cin >> n >> m;
    for (int i = 0; i <= n + 1; i++) insert(i);
    while (m--) {
        int l, r;
        cin >> l >> r;
        l = get_k(l), r = get_k(r + 2);
        splay(l, 0), splay(r, l);
        tr[tr[r].s[0]].flag ^= 1;
    }
    output(root);
    return 0;
}

树链剖分

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
using ll = long long;

const int maxn = 1e5 + 10, maxm = maxn * 2;

int n, m;
int w[maxn], h[maxn], e[maxm], ne[maxm], idx;
int id[maxn], nw[maxn], cnt;
int dep[maxn], siz[maxn], top[maxn], fa[maxn], son[maxn];

struct Tree {
    int l, r;
    ll add, sum;
}tr[maxn * 4];

void add(int a, int b) {
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}

void dfs1(int u, int f, int d) {
    dep[u] = d, fa[u] = f, siz[u] = 1;
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if (j == f) continue;
        dfs1(j, u, d + 1);
        siz[u] += siz[j];
        if (siz[son[u]] < siz[j]) son[u] = j;
    }
}

void dfs2(int u, int t) {
    id[u] = ++cnt, nw[cnt] = w[u], top[u] = t;
    if (!son[u]) return;
    dfs2(son[u], t);
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if (j == fa[u] || j == son[u]) continue;
        dfs2(j, j);
    }
}

void pushup(int u) {
    tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}

void pushdown(int u) {
    auto &root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1];
    if (root.add) {
        left.add += root.add, left.sum += root.add * (left.r - left.l + 1);
        right.add += root.add, right.sum += root.add * (right.r - right.l + 1);
        root.add = 0;
    }
}

void build(int u, int l, int r) {
    tr[u] = {l, r, 0, nw[r]};
    if (l == r) return;
    int mid = l + r >> 1;
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
    pushup(u);
}

void update(int u, int l, int r, int k) {
    if (l <= tr[u].l && r >= tr[u].r) {
        tr[u].add += k;
        tr[u].sum += k * (tr[u].r - tr[u].l + 1);
        return;
    }
    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    if (l <= mid) update(u << 1, l, r, k);
    if (r > mid) update(u << 1 | 1, l, r, k);
    pushup(u);
}

ll query(int u, int l, int r) {
    if (l <= tr[u].l && r >= tr[u].r) return tr[u].sum;
    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    ll res = 0;
    if (l <= mid) res += query(u << 1, l, r);
    if (r > mid) res += query(u << 1 | 1, l, r);
    return res;
}

void update_path(int u, int v, int k) {
    while (top[u] != top[v]) {
        if (dep[top[u]] < dep[top[v]]) swap(u, v);
        update(1, id[top[u]], id[u], k);
        u = fa[top[u]];
    }
    if (dep[u] < dep[v]) swap(u, v);
    update(1, id[v], id[u], k);
}

ll query_path(int u, int v) {
    ll res = 0;
    while (top[u] != top[v]) {
        if (dep[top[u]] < dep[top[v]]) swap(u, v);
        res += query(1, id[top[u]], id[u]);
        u = fa[top[u]];
    }
    if (dep[u] < dep[v]) swap(u, v);
    res += query(1, id[v], id[u]);
    return res;
}

void update_tree(int u, int k) {
    update(1, id[u], id[u] + siz[u] - 1, k);
}

ll query_tree(int u) {
    return query(1, id[u], id[u] + siz[u] - 1);
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> w[i];
    memset(h, -1, sizeof(h));
    for (int i = 1; i < n; i++) {
        int a, b;
        cin >> a >> b;
        add(a, b), add(b, a);
    }
    dfs1(1, -1, 1);
    dfs2(1, 1);
    build(1, 1, n);
    
    cin >> m;
    while (m--) {
        int t, u, v, k;
        cin >> t >> u;
        if (t == 1) {
            cin >> v >> k;
            update_path(u, v, k);
        }
        else if (t == 2) {
            cin >> k;
            update_tree(u, k);
        }
        else if (t == 3) {
            cin >> v;
            cout << query_path(u, v) << endl;
        }
        else cout << query_tree(u) << endl;
    }
    return 0;
}

Another树剖

using Edge = int;
struct HLD{
    int n;
    vector<int> sz, top, dep, fa, in, out, seq;
    vector<vector<Edge> > g;
    int ts;

    HLD(const vector<vector<Edge> > &g, int root = 1) : n(int(g.size()) - 1), g(g)  {
        ts = 0;
        sz.resize(n + 1);
        top.resize(n + 1);
        dep.resize(n + 1);
        fa.resize(n + 1);
        in.resize(n + 1);
        out.resize(n + 1);
        seq.resize(n + 1);
        dep[root] = 1;
        top[root] = root;
        dfs_sz(root);
        dfs_hld(root);
    }

    void dfs_sz(int u){
        if (fa[u]){
            for(auto it = g[u].begin(); it != g[u].end(); it++){
                if (*it == fa[u]){
                    g[u].erase(it);
                    break;
                }
            }
        }
        sz[u] = 1;
        for(auto &j : g[u]){
            fa[j] = u;
            dep[j] = dep[u] + 1;
            dfs_sz(j);
            sz[u] += sz[j];
            if (sz[j] > sz[g[u][0]])
                swap(j, g[u][0]);
        }
    }

    void dfs_hld(int u){
        in[u] = ++ts;
        seq[in[u]] = u;
        for (auto j : g[u]){
            top[j] = (j == g[u][0] ? top[u] : j);
            dfs_hld(j);
        }
        out[u] = ts;
    }

    int lca(int u, int v){
        while (top[u] != top[v]){
            if (dep[top[u]] > dep[top[v]]){
                u = fa[top[u]];
            } 
            else{
                v = fa[top[v]];
            }
        }
        return dep[u] < dep[v] ? u : v;
    }

    int dist(int u, int v){
        return dep[u] + dep[v] - 2 * dep[lca(u, v)];
    }

    bool in_subtree(int u, int v){
        return in[v] <= in[u] && in[u] <= out[v];
    }
    
    int jump(int u, int k) {
        if (dep[u] < k){
            return -1;
        }
        int d = dep[u] - k;
        while (dep[top[u]] > d){
            u = fa[top[u]];
        }
        return seq[in[u] - dep[u] + d];
    }
    
    int rooted_lca(int a, int b, int c){
        return lca(a, b) ^ lca(b, c) ^ lca(c, a);
    }

    template<typename Q>
    void modify_path(int u, int v, const Q &q, bool edge = false){
        while(top[u] != top[v]){
            if (dep[top[u]] < dep[top[v]]) swap(u, v);
            q(in[top[u]], in[u]);
            u = fa[top[u]];        
        }
        if (dep[u] > dep[v]) swap(u, v);
        q(in[u] + edge, in[v]);
    }

    template<typename Q>
    void modify_subtree(int u, const Q &q){
        q(in[u], out[u]);
    }  

    template<typename T, typename Q>
    T query_path(int u, int v, const Q &q, bool edge = false){
        T ret = T();
        while(top[u] != top[v]){
            if (dep[top[u]] < dep[top[v]]) swap(u, v);
            ret = q(in[top[u]], in[u]) + ret;
            u = fa[top[u]];
        }
        if (dep[u] > dep[v]) swap(u, v);
        return q(in[u] + edge, in[v]) + ret;
    }

    template<typename T, typename Q>
    T query_subtree(int u, const Q &q){
        return q(in[u], out[u]);
    }

    template<typename T, typename Q, typename F>
    T query_path_noncommutative(int u, int v, const Q &q, const F &f, bool edge = false){
        T left = T(), right = T();
        while(top[u] != top[v]){
            if (dep[top[u]] < dep[top[v]]) swap(u, v), swap(left, right);
            left = q(in[top[u]], in[u]) + left;
            u = fa[top[u]];
        }
        if (dep[u] > dep[v]) swap(u, v), swap(left, right);
        return f(left, q(in[u] + edge, in[v]) + right);
    }

};

struct Info{
    int lmn = 0, rmn = 0, lmx = 0, rmx = 0, mn = 0, mx = 0, sum = 0;
};

Info operator+(const Info &a, const Info &b){
    Info ret;
    ret.sum = a.sum + b.sum;
    ret.lmn = min(a.lmn, a.sum + b.lmn);
    ret.lmx = max(a.lmx, a.sum + b.lmx);
    ret.rmn = min(b.rmn, b.sum + a.rmn);
    ret.rmx = max(b.rmx, b.sum + a.rmx);
    ret.mn = min({a.mn, a.rmn + b.lmn, b.mn});
    ret.mx = max({a.mx, a.rmx + b.lmx, b.mx});
    return ret;
}

template<class Info>
struct SegmentTree{
    int n;
    vector<Info> info;

    SegmentTree() {}

    SegmentTree(int n, Info _init = Info()){
        init(vector<Info>(n, _init));
    }

    SegmentTree(const vector<Info> &_init){
        init(_init);
    }

    void init(const vector<Info> &_init){
        n = (int)_init.size();
        info.assign((n << 2) + 1, Info());
        function<void(int, int, int)> build = [&](int p, int l, int r){
            if (l == r){
                info[p] = _init[l - 1];
                return;
            }
            int m = (l + r) / 2;
            build(2 * p, l, m);
            build(2 * p + 1, m + 1, r);
            pull(p);
        };
        build(1, 1, n);
    }

    void pull(int p){
        info[p] = info[2 * p] + info[2 * p + 1];
    }

    void modify(int p, int l, int r, int x, const Info &v){
        if (l == r){
            info[p] = v;
            return;
        }
        int m = (l + r) / 2;
        if (x <= m){
            modify(2 * p, l, m, x, v);
        } 
        else{
            modify(2 * p + 1, m + 1, r, x, v);
        }
        pull(p);
    }

    void modify(int p, const Info &v){
        modify(1, 1, n, p, v);
    }

    Info query(int p, int l, int r, int x, int y){
        if (l > y || r < x){
            return Info();
        }
        if (l >= x && r <= y){
            return info[p];
        }
        int m = (l + r) / 2;
        return query(2 * p, l, m, x, y) + query(2 * p + 1, m + 1, r, x, y);
    }

    Info query(int l, int r){
        return query(1, 1, n, l, r);
    }

};

树套树

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

const int maxn = 5e4 + 10, maxm = maxn * 4, INF = 1e9;
int n, m, w[maxn];

struct Node {
    int l, r;
    multiset<int> s;
}tr[maxm];

void build(int u, int l, int r) {
    tr[u] = {l, r};
    tr[u].s.insert(-INF);
    for (int i = l; i <= r; i++) tr[u].s.insert(w[i]);
    if (l == r) return;
    int mid = l + r >> 1;
    build(u << 1, l, mid);
    build(u << 1 | 1, mid + 1, r);
}

void update(int u, int pos, int v) {
    tr[u].s.erase(tr[u].s.find(w[pos]));
    tr[u].s.insert(v);
    if (tr[u].l == tr[u].r) return;
    int mid = tr[u].l + tr[u].r >> 1;
    if (pos <= mid) update(u << 1, pos, v);
    else update(u << 1 | 1, pos, v);
}

int query(int u, int l, int r, int x) {
    if (tr[u].l >= l && tr[u].r <= r) {
        auto it = tr[u].s.lower_bound(x);
        return *prev(it);
    }
    int mid = tr[u].l + tr[u].r >> 1;
    int res = -INF;
    if (l <= mid) res = max(res, query(u << 1, l, r, x));
    if (r > mid) res = max(res, query(u << 1 | 1, l, r, x));
    return res;
}


int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> w[i];
    build(1, 1, n);
    while (m--) {
        int op, a, b, x;
        cin >> op;
        if (op == 1) {
            cin >> a >> x;
            update(1, a, x);
            w[a] = x;
        }
        else {
            cin >> a >> b >> x;
            cout << query(1, a, b, x) << endl;
        }
    }
    return 0;
}

外部线段树离散化开maxn * 4个点

内部线段树动态开maxn * log(n) * log(n)个点

#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
using ll = long long;

const int maxn = 5e4 + 10, maxm = maxn * 4;
int n, m;
int L[maxm], R[maxm], T[maxm], idx;

struct Query {
    int op, a, b, c;
}q[maxn];
vector<int> nums;

int get(int x) {
    return lower_bound(nums.begin(), nums.end(), x) - nums.begin();
}

namespace treeIn {

    struct inTree {
        int l, r;
        ll sum, add;
    };

    inTree tr2[maxn * 17 * 17];
    
    void pushup(int u) {
        tr2[u].sum = tr2[tr2[u].l].sum + tr2[tr2[u].r].sum;
    }

    void pushdown(int u, int l, int r) {
        if (!tr2[u].add) return;
        if (!tr2[u].l) tr2[u].l = ++idx;
        if (!tr2[u].r) tr2[u].r = ++idx;
        
        int mid = l + r >> 1;
        inTree &fa = tr2[u], &ls = tr2[tr2[u].l], &rs = tr2[tr2[u].r];
        ls.add += fa.add, rs.add += fa.add;
        ls.sum += fa.add * (mid - l + 1), rs.sum += fa.add * (r - mid);
        fa.add = 0;       
    }

    void modify2(int u, int l, int r, int ql, int qr) {
        if (ql <= l && r <= qr) {
            tr2[u].add++;
            tr2[u].sum += r - l + 1;
            return;
        }
        pushdown(u, l, r);
        int mid = l + r >> 1;
        if (ql <= mid) {
            if (!tr2[u].l) tr2[u].l = ++idx;
            modify2(tr2[u].l, l, mid, ql, qr);
        }
        if (qr > mid) {
            if (!tr2[u].r) tr2[u].r = ++idx;
            modify2(tr2[u].r, mid + 1, r, ql, qr);
        }
        pushup(u);
    }

    ll query2(int u, int l, int r, int ql, int qr) {
        if (ql <= l && r <= qr) return tr2[u].sum;
        pushdown(u, l, r);
        ll res = 0;
        int mid = l + r >> 1;
        if (ql <= mid) res += query2(tr2[u].l, l, mid, ql, qr);
        if (qr > mid) res += query2(tr2[u].r, mid + 1, r, ql, qr);
        return res;
    }
}
using namespace treeIn;

namespace treeOut {

    struct outTree {
        int l, r;
        int root;
    };

    outTree tr1[maxn * 4];

    void build(int u, int l, int r) {
        tr1[u] = {l, r, ++idx};
        if (l == r) return;
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
    }

    void modify1(int u, int l, int r, int x) {
        modify2(tr1[u].root, 1, n, l, r);
        if (tr1[u].l == tr1[u].r) return;
        int mid = tr1[u].l + tr1[u].r >> 1;
        if (x <= mid) modify1(u << 1, l, r, x);
        else modify1(u << 1 | 1, l, r, x);
    }

    int query1(int u, int l, int r, int k) {
        if (tr1[u].l == tr1[u].r) return tr1[u].l;
        ll cnt = query2(tr1[u << 1 | 1].root, 1, n, l, r);
        int mid = tr1[u].l + tr1[u].r >> 1;
        if (cnt >= k) return query1(u << 1 | 1, l, r, k);
        else return query1(u << 1, l, r, k - cnt);
    }
    
}
using namespace treeOut;

int main() {
    cin >> n >> m;
    for (int i = 0; i < m; i++) {
        cin >> q[i].op >> q[i].a >> q[i].b >> q[i].c;
        if (q[i].op == 1) nums.push_back(q[i].c);
    }
    sort(nums.begin(), nums.end());
    nums.erase(unique(nums.begin(), nums.end()), nums.end());
    build(1, 0, nums.size() - 1);
    for (int i = 0; i < m; i++) {
        int op = q[i].op, a = q[i].a, b = q[i].b, c = q[i].c;
        if (op == 1) modify1(1, a, b, get(c));
        else cout << nums[query1(1, a, b, c)] << endl;
    }
    return 0;
}

Link Cut Tree

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

const int maxn = 1e5 + 10;
int n, m;

struct Node {
    int s[2], p, v;
    int sum, rev;
}tr[maxn];
int stk[maxn];

void pushrev(int x) {
    swap(tr[x].s[0], tr[x].s[1]);
    tr[x].rev ^= 1;
}

void pushup(int x) {
    tr[x].sum = tr[tr[x].s[0]].sum ^ tr[x].v ^ tr[tr[x].s[1]].sum;
}

void pushdown(int x) {
    if (tr[x].rev) {
        pushrev(tr[x].s[0]), pushrev(tr[x].s[1]);
        tr[x].rev = 0;
    }
}

bool isroot(int x) {
    return tr[tr[x].p].s[0] != x && tr[tr[x].p].s[1] != x;
}

void rotate(int x) {
    int y = tr[x].p, z = tr[y].p;
    int k = tr[y].s[1] == x;
    if (!isroot(y)) tr[z].s[tr[z].s[1] == y] = x;
    tr[x].p = z;
    tr[y].s[k] = tr[x].s[k ^ 1], tr[tr[x].s[k ^ 1]].p = y;
    tr[x].s[k ^ 1] = y, tr[y].p = x;
    pushup(y), pushup(x); 
}

void splay(int x) {
    int top = 0, r = x;
    stk[++top] = r;
    while (!isroot(r)) stk[++top] = r = tr[r].p;
    while (top) pushdown(stk[top--]);
    while (!isroot(x)) {
        int y = tr[x].p, z = tr[y].p;
        if (!isroot(y)) {
            if ((tr[y].s[1] == x) ^ (tr[z].s[1] == y)) rotate(x);
            else rotate(y);
        }
        rotate(x);
    }
}

void access(int x) {
    int z = x;
    for (int y = 0; x; y = x, x = tr[x].p) {
        splay(x);
        tr[x].s[1] = y, pushup(x);
    }
    splay(z);
}

void makeroot(int x) {
    access(x);
    pushrev(x);
}

int findroot(int x) {
    access(x);
    while (tr[x].s[0]) pushdown(x), x = tr[x].s[0];
    splay(x);
    return x;
}

void split(int x, int y) {
    makeroot(x);
    access(y);
}

void link(int x, int y) {
    makeroot(x);
    if (findroot(y) != x) tr[x].p = y;
}

void cut(int x, int y) {
    makeroot(x);
    if (findroot(y) == x && tr[y].p == x && !tr[y].s[0]) {
        tr[x].s[1] = tr[y].p = 0;
        pushup(x);
    }
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> tr[i].v;
    while (m--) {
        int op, x, y;
        cin >> op >> x >> y;
        if (op == 0) {
            split(x, y);
            cout << tr[y].sum << endl;
        }
        else if (op == 1) link(x, y);
        else if (op == 2) cut(x, y);
        else {
            splay(x);
            tr[x].v = y;
            pushup(x);
        }
    }
    return 0;
}

点分治

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

const int maxn = 10010, maxm = maxn * 2;
int n, m;
int h[maxn], e[maxm], w[maxm], ne[maxm], idx;
bool vis[maxn];
int p[maxn], q[maxn];

void add(int a, int b, int c) {
    e[idx] = b;
    w[idx] = c;
    ne[idx] = h[a];
    h[a] = idx++;
}

int get_size(int u, int fa) {
    if (vis[u]) return 0;
    int res = 1;
    for (int i = h[u]; ~i; i = ne[i]) {
        if (e[i] != fa) res += get_size(e[i], u);
    }
    return res;
}

int get_wc(int u, int fa, int tot, int &wc) {
    if (vis[u]) return 0;
    int sum = 1, ms = 0;
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if (j == fa) continue;
        int t = get_wc(j, u, tot, wc);
        ms = max(ms, t);
        sum += t;
    } 
    ms = max(ms, tot - sum);
    if (ms <= tot / 2) wc = u;
    return sum;
}

void get_dis(int u, int fa, int dis, int &qt) {
    if (vis[u]) return;
    q[qt++] = dis;
    for (int i = h[u]; ~i; i = ne[i]) {
        if (e[i] != fa) {
            get_dis(e[i], u, dis + w[i], qt);
        }
    }
}

int get(int a[], int k) {
    sort(a, a + k);
    int res = 0;
    for (int i = k - 1, j = -1; i >= 0; i--) {
        while (j + 1 < i && a[j + 1] + a[i] <= m) j++;
        j = min(j, i - 1);
        res += j + 1;
    }
    return res;
}

int cal(int u) {
    if (vis[u]) return 0;
    int res = 0;
    get_wc(u, -1, get_size(u, -1), u);
    vis[u] = true;
    
    int pt = 0;
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i], qt = 0;
        get_dis(j, -1, w[i], qt);
        res -= get(q, qt);
        for (int k = 0; k < qt; k++) {
            if (q[k] <= m) res++;
            p[pt++] = q[k];
        }
    }
    res += get(p, pt);

    for (int i = h[u]; ~i; i = ne[i]) res += cal(e[i]);
    return res;
}

int main() {
    while (cin >> n >> m && (n || m)) {
        memset(vis, 0, sizeof(vis));
        memset(h, -1, sizeof(h));
        idx = 0;
        for (int i = 1; i < n; i++) {
            int a, b, c;
            cin >> a >> b >> c;
            add(a, b, c);
            add(b, a, c);
        }        
        cout << cal(0) << endl;
    }
    return 0;
}

点分树

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
using ll = long long;

const int maxn = 1.5e5 + 10, maxm = maxn * 2;
int n, m, A;
int h[maxn], e[maxm], ne[maxm], w[maxm], idx;
int age[maxn];
bool vis[maxn];

struct Father {
    int u, num; // 重心u 第num个子树
    ll dis; // 距离u dis
};
vector<Father> f[maxn];

struct Son {
    int age;
    ll dis;
    bool operator < (const Son& other) const {
        return age < other.age;
    }
};
vector<Son> son[maxn][3];

void add(int a, int b, int c) {
    e[idx] = b;
    w[idx] = c;
    ne[idx] = h[a];
    h[a] = idx++;
}

int get_size(int u, int fa) {
    if (vis[u]) return 0;
    int res = 1;
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if (j == fa) continue;
        res += get_size(j, u);
    }
    return res;
}

int get_wc(int u, int fa, int tot, int &wc) {
    if (vis[u]) return 0;
    int sum = 1, ms = 0;
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if (j == fa) continue;
        int t = get_wc(j, u, tot, wc);
        ms = max(ms, t);
        sum += t;
    }
    ms = max(ms, tot - sum);
    if (ms <= tot / 2) wc = u;
    return sum;
}

void get_dis(int u, int fa, ll dis, int wc, int k, vector<Son> &p) {
    if (vis[u]) return;
    f[u].push_back({wc, k, dis});
    p.push_back({age[u], dis});
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if (j == fa) continue;
        get_dis(j, u, dis + w[i], wc, k, p);
    }   
}

void cal(int u) {
    if (vis[u]) return;
    get_wc(u, -1, get_size(u, -1), u);
    vis[u] = true;
    
    for (int i = h[u], k = 0; ~i; i = ne[i]) {
        int j = e[i];
        if (vis[j]) continue;
        auto &p = son[u][k];
        p.push_back({-1, 0}), p.push_back({A + 1, 0});
        get_dis(j, -1, w[i], u, k, p);
        k++;
        sort(p.begin(), p.end());
        for (int i = 1; i < p.size(); i++) p[i].dis += p[i - 1].dis;
    }

    for (int i = h[u]; ~i; i = ne[i]) cal(e[i]);
}

ll query(int u, int l, int r) {
    ll res = 0;
    for (int i = 0; i < f[u].size(); i++) {
        auto &t = f[u][i];
        int g = age[t.u];
        if (g >= l && g <= r) res += t.dis;
        for (int j = 0; j < 3; j++) {
            if (j == t.num) continue;
            auto &p = son[t.u][j];
            if (p.empty()) continue;
            int a = lower_bound(p.begin(), p.end(), Son({l, -1})) - p.begin();
            int b = lower_bound(p.begin(), p.end(), Son({r + 1, -1})) - p.begin() - 1;
            res += t.dis * (b - a + 1) + p[b].dis - p[a - 1].dis;
        }
    }

    for (int i = 0; i < 3; i++) {
        auto &p = son[u][i];
        if (p.empty()) continue;
        int a = lower_bound(p.begin(), p.end(), Son{l, -1}) - p.begin();
        int b = lower_bound(p.begin(), p.end(), Son({r + 1, -1})) - p.begin() - 1;
        res += p[b].dis - p[a - 1].dis;
    }
    return res;
}

int main() {
    cin >> n >> m >> A;
    for (int i = 1; i <= n; i++) cin >> age[i];
    memset(h, -1, sizeof(h));
    for (int i = 1; i < n; i++) {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);
        add(b, a, c);
    }
    cal(1);
    ll res = 0;
    while (m--) {
        int u, a, b;
        cin >> u >> a >> b;
        int l = (a + res) % A, r = (b + res) % A;
        if (l > r) swap(l, r);
        res = query(u, l, r);
        cout << res << endl;
    }
    return 0;
}

CDQ分治

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

const int maxn = 100010, maxm = 2 * maxn;
int n, m;
struct Data {
	int a, b, c, s, res;

	bool operator < (const Data& t) const {
		if (a != t.a) return a < t.a;
		if (b != t.b) return b < t.b;
		return c < t.c;
	}

	bool operator == (const Data& t) const {
		return a == t.a && b == t.b && c == t.c;
	}
}q[maxn], w[maxn];
int tr[maxm], res[maxn];

int lowbit(int x) {
	return x & -x;
}

void add(int x, int v) {
	for (int i = x; i < maxm; i += lowbit(i)) tr[i] += v;
}

int query(int x) {
	int res = 0;
	for (int i = x; i; i -= lowbit(i)) res += tr[i];
	return res;
}

void merge_sort(int l, int r) {
	if (l >= r) return;
	int mid = l + r >> 1;
	merge_sort(l, mid), merge_sort(mid + 1, r);
	int i = l, j = mid + 1, k = 0;
	while (i <= mid && j <= r) {
		if (q[i].b <= q[j].b) add(q[i].c, q[i].s), w[k++] = q[i++];
		else q[j].res += query(q[j].c), w[k++] = q[j++];
	}
	while (i <= mid) add(q[i].c, q[i].s), w[k++] = q[i++];
	while (j <= r) q[j].res += query(q[j].c), w[k++] = q[j++];
	for (i = l; i <= mid; i++) add(q[i].c, -q[i].s);
	for (i = l, j = 0; j < k; i++, j++) q[i] = w[j];
}

int main() {
	cin >> n >> m;
	for (int i = 0; i < n; i++) {
		int a, b, c;
		cin >> a >> b >> c;
		q[i] =  {a, b, c, 1};
	}
	sort(q, q + n);
	int k = 1;
	for (int i = 1; i < n; i++) {
		if (q[i] == q[k - 1]) q[k - 1].s++;
		else q[k++] = q[i];
	}
	merge_sort(0, k - 1);
	for (int i = 0; i < k; i++) {
		res[q[i].res + q[i].s - 1] += q[i].s;
	}
	for (int i = 0; i < n; i++) cout << res[i] << endl;
	return 0;
}

DSU On Tree

// CF 600E
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
using ll = long long;

const int maxn = 1e5 + 10, maxm = maxn * 2;
int n;
vector<int> g[maxn];
int col[maxn], cnt[maxn], siz[maxn], son[maxn];
ll res[maxn], sum;
int mx;

void dfs1(int u, int fa) {
	siz[u] = 1;
	for (auto j : g[u]) {
		if (j == fa) continue;
		dfs1(j, u);
		siz[u] += siz[j];
		if (siz[j] > siz[son[u]]) son[u] = j;
	}
}

void update(int u, int fa, int sign, int pson) {
	int c = col[u];
	cnt[c] += sign;
	if (cnt[c] > mx) mx = cnt[c], sum = c;
	else if (cnt[c] == mx) sum += c;

	for (auto j : g[u]) {
		if (j == fa || j == pson) continue;
		update(j, u, sign, pson);
	}
}

void dfs2(int u, int fa, int op) {
	for (auto j : g[u]) {
		if (j == fa || j == son[u]) continue;
		dfs2(j, u, 0);
	}
	if (son[u]) dfs2(son[u], u, 1);
	update(u, fa, 1, son[u]);
	res[u] = sum;
	if (!op) update(u, fa, -1, 0), sum = mx = 0;
}

int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> col[i];
	for (int i = 1; i < n; i++) {
		int a, b;
		cin >> a >> b;
		g[a].push_back(b);
		g[b].push_back(a);
	}
	dfs1(1, 0);
	dfs2(1, 0, 1);
	for (int i = 1; i <= n; i++) cout << res[i] << " ";
	return 0;
}
#include <bits/stdc++.h>
using namespace std;
#define IOS                  \
ios::sync_with_stdio(false); \
cin.tie(0), cout.tie(0)
#define endl '\n'
using ll  = long long;
using PII = pair<int, int>;
using PLI = pair<ll, int>;
 
const int maxn = 2e5 + 10;
const int maxm = 2; 
const ll  INF = 1e18;
const int mod = 1e9 + 7;
int n, m, k, c;
vector<int> g[maxn];
int a[maxn];
set<int> se[maxn];
int res = 0;

void dfs(int u, int fa) {
    a[u] ^= a[fa];
    map<int, int> mp;
    int mx = 0;
    for (auto j : g[u]) {
        if (j == fa) continue;
        dfs(j, u);
        if (se[u].size() < se[j].size()) swap(se[u], se[j]);
        for (auto x : se[j]) {
            if (se[u].count(x)) {
                if (mp[x] == 0) mp[x] = 2;
                else mp[x]++;
                mx = max(mx, mp[x]);
            }
            else {
                se[u].insert(x);
            }
        }
    } 
    if (u > 1 && g[u].size() == 1) {
        res++;
        se[u].insert(a[u]);
    }
    if (mx > 1) {
        res -= mx - 1;
        se[u].clear();
        for (auto [x, y] : mp) {
            if (y == mx) se[u].insert(x);
        }
    }
}

void Jared_McDs() {
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i < n; i++) {
        int a, b;
        cin >> a >> b;
        g[a].push_back(b);
        g[b].push_back(a);
    }
    dfs(1, 0);
    res -= se[1].count(0);
    cout << res << endl;
}
 
int main() {
#ifdef LOCAL
    freopen("in.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif
    IOS;
    int w_ = 1;
   // cin >> w_;
    while (w_--) {
        Jared_McDs();
    }
    return 0;
}

Kruskal

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

const int maxn = 1e4 + 10, maxm = 5e4 + 10, INF = 1e9;
int n, m;
int p[maxn], h[maxn], e[maxm], ne[maxm], v[maxm], idx;
int dep[maxn], f[maxn], fa[maxn][21], w[maxn][21], vis[maxn];

struct Edge1 {
    int x, y, v;
    
    bool operator<(const Edge1 &oth) const {
        return v > oth.v;
    }
}edge1[maxm];


int find(int x) {
    if (p[x] != x) {
        return p[x] = find(p[x]);
    }
    return p[x];
}

void add(int a, int b, int c) {
    e[idx] = b;
    v[idx] = c;
    ne[idx] = h[a];
    h[a] = idx++;
}

void kruskal() {
    sort(edge1 + 1, edge1 + m + 1);
    for (int i = 1; i <= n; i++) p[i] = i;
    for (int i = 1; i <= m; i++) {
        int a = edge1[i].x, b = edge1[i].y, c = edge1[i].v;
        int pa = find(a), pb = find(b);
        if (pa != pb) {
            p[pa] = pb;
            add(a, b, c);
            add(b, a, c);
        }
    }
}

void dfs(int u) {
    vis[u] = 1;
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if (vis[j]) continue;
        dep[j] = dep[u] + 1;
        fa[j][0] = u;
        w[j][0] = v[i];
        dfs(j);
    }
}

int lca(int x, int y) {
    if (find(x) != find(y)) return -1;
    int res = INF;
    if (dep[x] > dep[y]) swap(x, y);
    for (int i = 20; i >= 0; i--) {
        if (dep[fa[y][i]] >= dep[x]) {
            res = min(res, w[y][i]);
            y = fa[y][i];
        }
    }
    if (x == y) return res;
    for (int i = 20; i >= 0; i--) {
        if (fa[x][i] != fa[y][i]) {
            res = min(res, min(w[x][i], w[y][i]));
            x = fa[x][i];
            y = fa[y][i];
        }
    }
    res = min(res, min(w[x][0], w[y][0]));
    return res;
}

int main() {
    memset(h, -1, sizeof(h));
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        cin >> edge1[i].x >> edge1[i].y >> edge1[i].v;
    }
    kruskal();
    
    for (int i = 1; i <= n; i++) {
        if (!vis[i]) {
            dep[i] = 1;
            dfs(i);
            fa[i][0] = i;
            w[i][0] = INF;
        }
    }

    for (int i = 1; i <= 20; i++) {
        for (int j = 1; j <= n; j++) {
            fa[j][i] = fa[fa[j][i - 1]][i - 1];
            w[j][i] = min(w[j][i - 1], w[fa[j][i - 1]][i - 1]);
        }
    }

    int q;
    cin >> q;
    while (q--) {
        int x, y;
        cin >> x >> y;
        cout << lca(x, y) << endl;
    }
    return 0;
}

图论

最短路

//多源最短路 Floyd算法(O(n^3))
//本质上是一个动态规划的思想,每一次循环更新经过前k个节点,i到j的最短路径。
int dis[400][400];
void floyd(int n)
{
    for (int k = 1; k <= n; k++)
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}
//Floyd初始化
memset(dis, 0x3f, sizeof(dis));
//利用memset的特性,先把所有距离初始化为0x3f3f3f3f,注意这个数的两倍小于32位和64位机器上的INT_MAX
for (int i = 1; i <= n; ++i)
    dis[i][i] = 0;
for (int i = 0; i < m; ++i)
{
    int u, v, w;
    cin >> u >> v >> w;
    dis[u][v] = w;
}
//____________________________________________________________________________________________________________________________
// 单源最短路
// 1. Bellman-Ford算法(O(m*n)) 处理负权边
// 把所有边松弛 n - 1 遍
int dis[N], backup[N];

void Bellman_Ford() {
    memset(dis, 0x3f, sizeof(dis));
    dis[1] = 0;
    for (int j = 0; j < n - 1; ++j) {
        memcpy(backup, dis, sizeof(dis));
        for (int i = 1; i <= m; ++i)
            dis[edges[i].to] = min(dis[edges[i].to], backup[edges[i].from] + edges[i].w);
    }
}

//____________________________________________________________________________________________________________________________
// 2. SPFA算法
// 队列优化的Bellman-Ford算法
// 每次不松弛所有点,而只松弛可能更新的点?观察发现,第一次松弛S,P1时,可能更新的点只可能是S能直接到达的点。然后下一次可能被更新的则是S能直接到达的点能直接到达的点
// 时间复杂度不稳定,最坏情况可以被卡成Bellman-Ford
// SPFA也可以判负权环,我们可以用一个数组记录每个顶点进队的次数,当一个顶点进队超过n - 1次时,就说明存在负权

void spfa() {
    queue<int> q;
    memset(dis, 0x3f, sizeof(dis));
    dis[1] = 0;
    q.push(1);
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        vis[u] = false;
        for (int i = 0; i < to[u].size(); ++i) { //遍历邻接的顶点
            int v = to[u][i], w = edge[u][i];
            if (dis[v] > dis[u] + w) {
                dis[v] = dis[u] + w;
                if (!vis[v]) {
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
    }
}

//____________________________________________________________________________________________________________________________
// 3. Dijkstra算法
//Dij基于一种贪心的思想,处理一张没有负边的图
// 朴素O(n^2), 堆优化O(mlogn)

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

const int maxn = 110, maxm = 410, INF = 0x3f3f3f3f;
int n, m, g[maxn][maxn], dis[maxn];
bool vis[maxn];

void dijkstra() {
    memset(dis, 0x3f, sizeof(dis));
    dis[1] = 0;
    for (int i = 1; i <= n; i++) {
        int t = -1;
        for (int j = 1; j <= n; j++) {
            if (!vis[j] && (t == -1 || dis[j] < dis[t])) {
                t = j;
            }
        }
        vis[t] = true;
        for (int j = 1; j <= n; j++) {
            dis[j] = min(dis[j], dis[t] + g[t][j]);
        }
    }
}

// 堆优化
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
using PII = pair<int, int>;

const int maxn = 2510, maxm = 6210 * 2;
int h[maxn], e[maxm], ne[maxm], w[maxm], idx;
int dis[maxn];
bool vis[maxn];
int n, m, s, t;

void add(int a, int b, int c) {
    e[idx] = b;
    w[idx] = c;
    ne[idx] = h[a];
    h[a] = idx++;
}

void dijkstra() {
    memset(dis, 0x3f, sizeof(dis));
    memset(vis, 0, sizeof(vis));
    priority_queue<PII, vector<PII>, greater<PII>> q;
    q.push({0, s});
    dis[s] = 0;
    while (q.size()) {
        PII tmp = q.top();
        q.pop();
        if (vis[tmp.second]) continue;
        vis[tmp.second] = true;
        for (int i = h[tmp.second]; ~i; i = ne[i]) {
            int j = e[i];
            if (dis[j] > tmp.first + w[i]) {
                dis[j] = tmp.first + w[i];
                q.push({dis[j], j});
            }
        }
    }
}

最小生成树

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

const int maxn = 110;
int g[maxn][maxn], dis[maxn];
bool vis[maxn];
int n;

int prim() {
    int res = 0;
    memset(dis, 0x3f, sizeof(dis));
    dis[1] = 0;
    for (int i = 1; i <= n; i++) {
        int t = -1;
        for (int j = 1; j <= n; j++) {
            if (!vis[j] && (t == -1 || dis[t] > dis[j])) {
                t = j;
            }
        }
        vis[t] = true;
        res += dis[t];
        for (int j = 1; j <= n; j++) {
            dis[j] = min(dis[j], g[t][j]);
        }
    }
    return res;
}


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

const int maxn = 110, maxm = 210;
int p[maxn], n, m;

struct Edge {
    int a, b, w;
    bool operator < (const Edge& oth) const {
        return w < oth.w;
    }
}edge[maxm];

int find(int x) {
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) p[i] = i;
    for (int i = 0; i < m; i++) {
        int a, b, w;
        cin >> a >> b >> w;
        edge[i] = {a, b, w};
    }
    sort(edge, edge + m);

    int res = 0;
    for (int i = 0; i < m; i++) {
        int a = find(edge[i].a), b = find(edge[i].b), w = edge[i].w;
        if (a != b) p[a] = b;
        else res += w;
    }
    cout << res << endl;
    return 0;
}


// 严格次小生成树
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
using ll = long long;

const int maxn = 510, maxm = 1e4 + 10;
int p[maxn], n, m;
int h[maxn], e[maxn * 2], ne[maxn * 2], w[maxn * 2], idx;
int dis1[maxn][maxn], dis2[maxn][maxn];

struct Edge {
    int a, b, w;
    bool flag = false;
    bool operator < (const Edge& oth) const {
        return w < oth.w;
    }
}edges[maxm];

void add(int a, int b, int c) {
    e[idx] = b;
    w[idx] = c;
    ne[idx] = h[a];
    h[a] = idx++;
}

int find(int x) {
    if (p[x] != x) return p[x] = find(p[x]);
    return p[x];
}

void dfs(int u, int fa, int maxd1, int maxd2, int d1[], int d2[]) {
    d1[u] = maxd1, d2[u] = maxd2;
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if (j == fa) continue;
        int td1 = maxd1, td2 = maxd2;
        if (w[i] > td1) td2 = td1, td1 = w[i];
        else if (w[i] < td1 && w[i] > td2) td2 = w[i];
        dfs(j, u, td1, td2, d1, d2);
    }
}

int main() {
    memset(h, -1, sizeof(h));
    cin >> n >> m;
    for (int i = 0; i < m; i++) {
        int a, b, w;
        cin >> a >> b >> w;
        edges[i] = { a, b, w };
    }
    sort(edges, edges + m);
    for (int i = 1; i <= n; i++) p[i] = i;
    ll sum = 0;
    for (int i = 0; i < m; i++) {
        int a = edges[i].a, b = edges[i].b, w = edges[i].w;
        int pa = find(a), pb = find(b);
        if (pa != pb) {
            sum += w;
            p[pa] = pb;
            add(a, b, w);
            add(b, a, w);
            edges[i].flag = true;
        }
    }

    for (int i = 1; i <= n; i++) dfs(i, -1, -1e9, -1e9, dis1[i], dis2[i]);

    ll res = 1e18;
    for (int i = 0; i < m; i++) {
        int a = edges[i].a, b = edges[i].b, w = edges[i].w;
        if (edges[i].flag) continue;
        ll tmp = 1e18;
        if (w > dis1[a][b]) {
            tmp = sum + w - dis1[a][b];
        }
        else if (w > dis2[a][b]) {
            tmp = sum + w - dis2[a][b];
        }
        res = min(res, tmp);
    }
    cout << res << endl;       
    return 0;
}

LCA

一组节点的LCA是DFS序最小和最大的两点的LCA

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

const int maxn = 4e4 + 10, maxm = maxn * 2;
int h[maxn], e[maxm], ne[maxm], idx;
int dep[maxn], fa[maxn][16], q[maxn];

void add(int a, int b) {
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}

void bfs(int root) {
    memset(dep, 0x3f, sizeof(dep));
    dep[0] = 0, dep[root] = 1;
    int hh = 0, tt = 0;
    q[0] = root;
    while (hh <= tt) {
        int t = q[hh++];
        for (int i = h[t]; ~i; i = ne[i]) {
            int j = e[i];
            if (dep[j] > dep[t] + 1) {
                dep[j] = dep[t] + 1;
                q[++tt] = j;
                fa[j][0] = t;
                for (int k = 1; k <= 15; k++) {
                    fa[j][k] = fa[fa[j][k - 1]][k - 1];
                }
            }
        }
    }
}

int lca(int a, int b) {
    if (dep[a] < dep[b]) swap(a, b);
    for (int k = 15; k >= 0; k--) {
        if (dep[fa[a][k]] >= dep[b]) {
            a = fa[a][k];
        }
    }
    if (a == b) return a;
    for (int k = 15; k >= 0; k--) {
        if (fa[a][k] != fa[b][k]) {
            a = fa[a][k];
            b = fa[b][k];
        }
    }
    return fa[a][0];
}

int main() {
    int n;
    cin >> n;
    memset(h, -1, sizeof(h));

    int root;
    for (int i = 1; i <= n; i++) {
        int a, b;
        cin >> a >> b;
        if (b == -1) root = a;
        else add(a, b), add(b, a);
    }

    bfs(root);

    int m;
    cin >> m;
    while (m--) {
        int a, b;
        cin >> a >> b;
        int p = lca(a, b);
        if (p == a) cout << 1 << endl;
        else if (p == b) cout << 2 << endl;
        else cout << 0 << endl;
    }
    return 0;
}

匈牙利算法

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

const int N = 100010, M = 200010;
int n, m;
int h[N], e[M], ne[M], idx;
int color[N];

void add(int a, int b) {
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx;
    idx++;
}

// 染色法判断二分图
bool dfs(int u, int c) {
    color[u] = c;
    for (int i = h[u]; i != -1; i = ne[i]) {
        int j = e[i];
        if (!color[j]) {
            if (!dfs(j, 3 - c)) return false;
        }
        else if (color[j] == c) return false;
    }
    return true;
}

bool st[N];
int match[N];

bool find(int x) {
    for (int i = h[x]; i != -1; i = ne[i]) {
        int j = e[i];
        if (!st[j]) {
            st[j] = true;
            if (match[j] == 0 || find(match[j])) {
                match[j] = x;
                return true;
            }
        }
    }
    return false;
}

int main() {
    cin >> n >> m;
    memset(h, -1, sizeof(h));
    while (m--) {
        int a, b;
        cin >> a >> b;
        add(a, b), add(b, a);
    }
    // 判断
    bool flag = true;
    for (int i = 1; i <= n; i++) {
        if (!color[i]) {
            if (!dfs(i, 1)) {
                flag = false;
                break;
            }
        }
    }
    if (flag) cout << "Yes" << endl;
    else cout << "No" << endl;
    // 匈牙利
    int res = 0;
    for (int i = 1; i <= n; i++) {
        memset(st, false, sizeof(st));
        if (find(i)) res++;
    }
    cout << res << endl;
}

无向图求环

vector<int> g[maxn], cycle;
bool v[maxn], st[maxn], oncycle[maxn];
int fa[maxn];

void dfs(int u, int st) {
    if (cycle.size()) return;
    v[u] = 1;
    for (auto j : g[u]) {
        if (j == fa[u]) continue;
        if (cycle.size()) return;
        if (j == st) {
            int t = u;
            while (t != j) {
                cycle.push_back(t);
                oncycle[t] = 1;
                t = fa[t];
            }
            cycle.push_back(j);
            oncycle[j] = 1;
            reverse(cycle.begin(), cycle.end());
            return;
        }
        if (!v[j]) {
            fa[j] = u;
            dfs(j, st);
        }
    }
}

网络流

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

const int maxn = 10010, maxm = 200010, INF = 1e9;
int n, m, S, T;
int h[maxn], e[maxm], f[maxm], ne[maxm], idx;
int q[maxn], d[maxn], cur[maxn];

void add(int a, int b, int c) {
	e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx++;
	e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx++;
}

bool bfs() {
	int hh = 0, tt = 0;
	memset(d, -1, sizeof(d));
	q[0] = S, d[S] = 0, cur[S] = h[S];
	while (hh <= tt) {
		int t = q[hh++];
		for (int i = h[t]; ~i; i = ne[i]) {
			int v = e[i];
			if (d[v] == -1 && f[i]) {
				d[v] = d[t] + 1;
				cur[v] = h[v];
				if (v == T) return true;
				q[++tt] = v;
			}
		}
	}
	return false;
}

int find(int u, int limit) {
	if (u == T) return limit;
	int flow = 0;
	for (int i = cur[u]; ~i && flow < limit; i = ne[i]) {
		cur[u] = i;
		int v = e[i];
		if (d[v] == d[u] + 1 && f[i]) {
			int t = find(v, min(f[i], limit - flow));
			if (!t) d[v] = -1;
			f[i] -= t, f[i ^ 1] += t, flow += t;
		}
	}
	return flow;
}

int dinic() {
	int res = 0, flow;
	while (bfs()) {
		while (flow = find(S, INF)) {
			res += flow;
		}
	}
	return res;
}

int main() {
	cin >> n >> m >> S >> T;
	memset(h, -1, sizeof(h));
	while (m--) {
		int a, b, c;
		cin >> a >> b >> c;
		add(a, b, c);
	}
	cout << dinic() << endl;
	return 0;
}

有源上下界最大/小流

创建虚拟源点S和T补流, 又因为原图有自带的源汇点(s, t), 因此源汇点不保证流量守恒(其他店保证),所以包补一条t到s的满容量边让远点流量守恒

int main() {
	int s, t;
    cin >> n >> m >> s >> t;
    S = 0, T = n + 1;
    memset(h, -1, sizeof(h));
    while (m--) {
        int a, b, c, d;
        cin >> a >> b >> c >> d;
        add(a, b, d - c);
        A[a] -= c, A[b] += c;
    }
    int tot = 0;
    for (int i = 1; i <= n; i++) {
        if (A[i] > 0) add(S, i, A[i]), tot += A[i];
        else if (A[i] < 0) add(i, T, -A[i]);
    }
    add(t, s, INF);
    if (dinic() < tot) {
        cout << "No Solution" << endl;
        return 0;
    }
    S = s, T = t;
    int res = f[idx - 1];
    f[idx - 1] = f[idx - 2] = 0;
    cout << res + dinic() << endl;
    return 0;
}

最大权闭合图

新建一个S点对于权值>0的点连边,新建一个T点对于权值<0的点连边(p,t)。结论是:最大权闭合子图的值=所有的正权值点和-最小割。

int main() {
    cin >> n >> m;
    S = 0, T = n + m + 1;
    memset(h, -1, sizeof(h));
    for (int i = 1; i <= n; i++) {
        int x;
        cin >> x;
        add(m + i, T, x);
    }
    int sum = 0;
    for (int i = 1; i <= m; i++) {
        int a, b, c;
        cin >> a >> b >> c;
        add(S, i, c);
        add(i, m + a, INF);
        add(i, m + b, INF);
        sum += c;
    }
    cout << sum - dinic() << endl;
    return 0;  
}

最大密度子图

最大化边的数量/点的数量

费用流

template<typename cost_t>
struct MinCostMaxFlow {
 
    const cost_t INF = numeric_limits<cost_t>::max() / 2;
    int h[maxn], e[maxm], ne[maxm], idx;
    cost_t f[maxm], w[maxm], d[maxn], incf[maxn];
    int q[maxn], pre[maxn];
    bool vis[maxn];
    int V, S, T;
 
    void init(int v, int s, int t) {
        for(int i = 0; i <= v; i++) h[i] = -1;
        idx = 0;
        V = v, S = s, T = t;
    }
 
    void add(int a, int b, cost_t c, cost_t d) {
        e[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx++;
        e[idx] = a, f[idx] = 0, w[idx] = -d, ne[idx] = h[b], h[b] = idx++;
    }
 
    bool spfa() {
        int hh = 0, tt = 0;
        for(int i = 0; i <= V; i++){
            d[i] = INF;
            incf[i] = 0;
            vis[i] = 0;
        }
        q[tt++] = S, d[S] = 0, incf[S] = INF;
        while(hh != tt) {
            int t = q[hh++];
            if (hh == maxn) hh = 0;
            vis[t] = 0;
            for(int i = h[t]; ~i; i = ne[i]) {
                int j = e[i];
                if (f[i] && d[j] > d[t] + w[i]){
                    d[j] = d[t] + w[i];
                    incf[j] = min(incf[t], f[i]);
                    pre[j] = i;
                    if (!vis[j]){
                        vis[j] = 1;
                        q[tt++] = j;
                        if (tt == maxn) tt = 0;
                    }
                }
            }
        }
        return incf[T] > 0;
    }
 
    pair<cost_t, cost_t> EK() {
        cost_t flow = 0, cost = 0;
        while(spfa()){
            cost_t t = incf[T];
            flow += t, cost += d[T] * t;
            for(int i = T; i != S; i = e[pre[i] ^ 1]) {
                f[pre[i]] -= t, f[pre[i] ^ 1] += t;
            }
        }
        return {flow, cost};
    }
};
MinCostMaxFlow<int> flow;

2-Sat

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

const int maxn = 2e6 + 10, maxm = 2e6 + 10;
int n, m;
int h[maxn], e[maxm], ne[maxm], idx;
int dfn[maxn], low[maxn], ts, stk[maxn], top;
int id[maxn], cnt;
bool ins[maxn];

void add(int a, int b) {
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}

void tarjan(int u) {
    dfn[u] = low[u] = ++ts;
    stk[++top] = u, ins[u] = true;
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if (!dfn[j]) {
            tarjan(j);
            low[u] = min(low[u], low[j]);
        }
        else if (ins[j]) low[u] = min(low[u], dfn[j]);
    }
    if (low[u] == dfn[u]) {
        int y;
        cnt++;
        do {
            y = stk[top--], ins[y] = false, id[y] = cnt;
        } while (y != u);
    }
}

int main() {
    ios::sync_with_stdio(false); 
    cin.tie(0), cout.tie(0);
    cin >> n >> m;
    memset(h, -1, sizeof(h));
    while (m--) {
        int i, a, j, b;
        cin >> i >> a >> j >> b;
        i--, j--;
        add(2 * i + !a, 2 * j + b);
        add(2 * j + !b, 2 * i + a);
    }
    for (int i = 0; i < 2 * n; i++) {
        if (!dfn[i]) tarjan(i);
    }
    for (int i = 0; i < n; i++) {
        if (id[2 * i] == id[2 * i + 1]) {
            cout << "IMPOSSIBLE" << endl;
            return 0;
        }
    }
    cout << "POSSIBLE" << endl;
    for (int i = 0; i < n; i++) {
        if (id[2 * i] < id[2 * i + 1]) cout << "0 ";
        else cout << "1 ";
    }
    return 0;
}


struct SCC{
	int h[maxn], hn[maxn], e[maxm], ne[maxm], idx;
	int dfn[maxn], low[maxn], ts;
	int stk[maxn], top;
	int scc_cnt, id[maxn];
	vector<int> scc[maxn];
	int n; 
	bool ins[maxn];
	
	void add(int h[], int a, int b){
	    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
	}
	
	void add(int a, int b){
		add(h, a, b);
	}
	
	void tarjan(int u){
	    dfn[u] = low[u] = ++ts;
	    stk[++top] = u;
	    ins[u] = 1;
	    for(int i = h[u]; ~i; i = ne[i]){
	        int j = e[i];
	        if (!dfn[j]){
	            tarjan(j);
	            low[u] = min(low[u], low[j]);
	        }
	        else if (ins[j]) low[u] = min(low[u], dfn[j]);
	    }
	    if (dfn[u] == low[u]){
	        ++scc_cnt;
	        int y;
	        do{
	            y = stk[top--];
	            id[y] = scc_cnt;
	            ins[y] = 0;
	            scc[scc_cnt].push_back(y);
	        }while(y != u);
	    }
	}
	
	void init(int _n){
        n = _n;
	    for(int i = 1; i <= n; i++){
	    	scc[i].clear();
	        dfn[i] = 0;
	        h[i] = hn[i] = -1;
	    }
	    scc_cnt = top = ts = idx = 0;
	}
	
	void build(){
		for(int i = 1; i <= n; i++)
			if (!dfn[i]) tarjan(i); 
	    for(int x = 1; x <= n; x++)
	        for(int i = h[x]; ~i; i = ne[i]){
	            int j = e[i];
	            if (id[j] != id[x]) add(hn, id[x], id[j]);
	        }
	}
	
	void topsort(){
        for(int x = scc_cnt; x; x--){
 
        }
	}
}scc;

数学

线性筛

#include <iostream>
using namespace std;

const int maxn = 1e6 + 10;
int isprime[maxn], prime[maxn], cnt;

void init() {
    isprime[1] = true;
    for (int i = 2; i < maxn; i++) {
        if (!isprime[i]) prime[cnt++] = i;
        for (int j = 0; prime[j] <= maxn / i; j++) {
            isprime[prime[j] * i] = true;
            if (i % prime[j] == 0) break;
        }
    }
}

欧几里得

// 2. 欧几里得算法(或辗转相除法
//辗转相除法,求两个数的最大公因数
//gcd(m,n) = gcd(m + kn,n)
//gcd(a, gcd(b, c)) == gcd(gcd(a, b), c)
//O(log(min(m,n)))
int gcd(int a, int b)
{
    if (b == 0)
        return a;
    else
        return gcd(b, a % b);
}                       
// 拓展欧几里德 可以在辗转相除途中求出不定方程 ax + by = c 的一组解。
// 裴蜀定理 : 设 a,b 为正整数,则关于x,y的方程 ax + by = c有整数解当且仅当c是gcd(a,b)的倍数。
// x = x0 + kb / d, y = y0 + ka / d
int exgcd(int a, int b, int &x, int &y)// 拓欧
{
    if (b == 0)
    {
        x = 1;
        y = 0;
        return a;
    }
    int d = exgcd(b, a % b, y, x);
    y -= (a / b) * x;
    return d;
}
// 求逆元
// ab == 1(mod p) a = inv(b)
// a/b = a * inv(b)(mod p)
// ax + by = 1   a * x = 1(mod b)

// ax = 1 (mod b) 则x为a的逆元
// a ^ (b - 2) 要求b为素数
// exgcd(a, p, x, y)中的x 要求gcd(a, p) == 1
int inv(int a, int p)
{
    int x, y;
    if (exgcd(a, p, x, y) != 1) // 无解的情形
        return -1;
    return (x % p + p) % p;
}

中国剩余定理

/*
    x % a[1] = m[1]
    x % a[2] = m[2]
    x % a[3] = m[3]
        ......
    x % a[n] = m[n]
    要求m[1] ... m[n]两两互质
*/
#include <iostream>
using namespace std;
using ll = long long;

ll exgcd(ll a, ll b, ll &x, ll &y) {
    if (b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    ll d = exgcd(b, a % b, y, x);
    y -= (a / b) * x;
    return d;
}

ll inv(ll a, ll p) {
    ll x, y;
    exgcd(a, p, x, y);
    return (x % p + p) % p;
}

inline ll CRT(ll m[], ll a[], ll n) {
    ll M = 1, x = 0;
    for (int i = 0; i < n; i++) M *= m[i];
    for (int i = 0; i < n; i++) {
        ll Mi = M / m[i];
        x += (a[i] * Mi * inv(Mi, m[i])) % M;
    }
    return x % M;
}

组合数

// O(max(a, b) ^ 2)
// C(a, b) = C(a - 1, b) + C(a - 1, b - 1)
const int maxn = 2010;
int c[2010][2010];
const int mod = 1e9 + 7;
using ll = long long; 

void init() {
    for (int i = 0; i < maxn; i++) {
        for (int j = 0; j < maxn; j++) {
            if (!j) c[i][j] = 0;
            else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
        }
    }
}

// O(nlogn)
int fact[maxn],infact[maxn];
// 费马小定理 若p是质数且gcd(a,p) = 1, 则有 a ^ (p - 1) = 1 (mod p)
// 逆元inv(a) = a ^ (p - 2)(mod p)
ll ksm(int a, int b, int mod)
{
     ll res = 1;
     while(b)
     {
         if(b & 1) res = res * a % mod;
         b >>= 1;
         a = (ll)a * a % mod;
     }
     return res;
}

void init()
{
    fact[0] = infact[0] = 1;
    for(int i = 1; i < maxn; i++)
    {
        fact[i] = (ll)fact[i - 1] * i % mod;
        infact[i] = (ll)infact[i - 1] * ksm(i, mod - 2, mod) % mod;
    }
}

ll C(int a,int b) {
    if(b > a || b < 0) return 0;
    return (ll)fact[a] * infact[b] % mod * infact[a - b] % mod; 
}

ll divide(int a, int b) {
    if (a == 0 && b == 0) return 1; //挖个坑
    return C(a - 1, b - 1);
}

// Lucas定理 O(mod)
// C(a, b) = C(a % mod, b % mod) * C(a / p, b / p) (mod p)

int C(int a, int b) {
    int res = 1, inv = 1;
    for (int i = 1, j = a; i <= b; i++, j--) {
        res = (ll)res * j % mod;
        inv = (ll)inv * i % mod;
    }
    res = (ll)res * ksm(inv, mod - 2) % mod;
    return res;
}

int lucas(ll a, ll b) {
    if (a < mod && b < mod) return C(a, b);
    return (ll)C(a % mod, b % mod) * lucas(a / mod, b / mod) % mod;
}


// 卡特兰数 C(2n, n) / (n + 1)

获得所有因数

#include <iostream>
#include <vector>
using namespace std;
using ll = long long;
using PII = pair<int, int>;

const int maxn = 5e4 + 10;
int primes[maxn], isprime[maxn], cnt;
int divisor[1601], dcnt;
vector<PII> factor;

void init(int n) {
    for (int i = 2; i <= n; i++) {
        if (!isprime[i]) primes[cnt++] = i;
        for (int j = 0; primes[j] <= n / i; j++) {
            isprime[primes[j] * i] = true;
            if (i % primes[j] == 0) break;
        }
    }
}

void dfs(int u, int num) {
    if (u == factor.size()) {
        divisor[dcnt++] = num;
        return;
    }
    for (int i = 0; i <= factor[u].second; i++) {
        dfs(u + 1, num);
        num *= factor[u].first;
    }
}

int gcd(int a, int b) {
    return b ? gcd(b, a % b) : a;
}

int main() {
    init(maxn - 1);
    int T;
    cin >> T;
    while (T--) {
        factor.clear();
        int a, b, c, d;
        cin >> a >> b >> c >> d;
        int tmp = d;
        for (int i = 0; primes[i] <= tmp / primes[i]; i++) {
            int p = primes[i];
            if (tmp % p == 0) {
                int num = 0;
                while (tmp % p == 0) tmp /= p, num++;
                factor.push_back({p, num});
            } 
        }
        if (tmp > 1) factor.push_back({tmp, 1});
        dcnt = 0;
        dfs(0, 1);

        int res = 0;
        for (int i = 0; i < dcnt; i++) {
            int x = divisor[i];
            if (gcd(a, x) == b && (ll)c * x / gcd(c, x) == d) res++;
        }
        cout << res << endl;
    }
    return 0;
}

欧拉函数

// n为质数, 欧拉函数为n - 1
// 欧拉函数是积性函数, 当gcd(a, b) = 1, φ(ab) = φ(a) * φ(b)
// 特别地, n为奇数时, φ(2n) = φ(n)
// n = Σ(d|n) φ(d)
// n = p ^ k(p为质数), φ(n) = p ^ k - p ^ (k - 1)
// n = ∏(i = 1, s)pi ^ ki, φ(n) = n * ∏(i = 1, s) (pi - 1) / pi;
// 欧拉定理 gcd(a, m) = 1, a ^ (φ(m)) = 1(mod m)
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 1e5 + 5;

int main() {
    int n;
    cin >> n;
    int res = n;
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) {
            res = res / i * (i - 1);
            while (n % i == 0) n /= i;
        }
    }
    if (n > 1) res = res / n * (n - 1);
    cout << res << endl;

}

#include <iostream>
using namespace std;

const int maxn = 1010;
int isprime[maxn], primes[maxn], cnt, phi[maxn];

void init(int n) {
    isprime[1] = 1;
    phi[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (!isprime[i]) primes[cnt++] = i, phi[i] = i - 1;
        for (int j = 0; primes[j] <= n / i; j++) {
            isprime[primes[j] * i] = true;
            if (i % primes[j] == 0) {
                phi[primes[j] * i] = primes[j] * phi[i];
                break;
            }
            phi[primes[j] * i] = (primes[j] - 1) * phi[i];
        }
    }
}

矩阵乘法

#include <iostream>
#include <cstring>
using namespace std;
using ll = long long;

const int maxn = 25;
int n, m, mod;
char s[maxn];
int nxt[maxn], a[maxn][maxn];

void qmul(int a[][maxn], int b[][maxn], int c[][maxn]) {
    static int t[maxn][maxn];
    memset(t, 0, sizeof(t));
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < m; j++) {
            for (int k = 0; k < m; k++) {
                t[i][j] = (t[i][j] + (ll)a[i][k] * b[k][j] % mod) % mod;
            }
        }
    }
    memcpy(c, t, sizeof(t));
}


int main() {
    cin >> n >> m >> mod >> s + 1;
    for (int i = 2, j = 0; i <= m; i++) {
        while (j && s[j + 1] != s[i]) j = nxt[j];
        if (s[j + 1] == s[i]) j++;
        nxt[i] = j;
    }
    for (int j = 0; j < m; j++) {
        for (int c = '0'; c <= '9'; c++) {
            int k = j;
            while (k && s[k + 1] != c) k = nxt[k];
            if (s[k + 1] == c) k++;
            if (k < m) a[j][k]++;
        }
    }

    int f[maxn][maxn] = {1};
    while (n) {
        if (n & 1) qmul(f, a, f);
        qmul(a, a, a);
        n >>= 1;
    }
    int res = 0;
    for (int i = 0; i < m; i++) res = (res + f[0][i]) % mod;
    cout << res << endl;

    return 0;   
}

高斯消元

// O(n ^ 3) 求解包含n个方程和n个未知数多元线性方程组
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;

const int N = 110;
const double eps = 1e-6;
double a[N][N];
int n;

// 0 唯一解 1 无穷多解 2 无解 
int gauss() {
    int c, r;
    for (c = 0, r = 0; c < n; c++, r++) {
        int t = r;
        for (int i = r; i < n; i++) {
            if (fabs(a[i][c]) > fabs(a[t][c])) t = i;
        }
        if (fabs(a[t][c]) < eps) break;
        for (int i = c; i <= n; i++) swap(a[t][i], a[r][i]);
        
        for (int i = n; i >= c; i--) a[r][i] /= a[r][c];
        for (int i = r + 1; i < n; i++) {
            if (fabs(a[i][c]) > eps) {
                for (int j = n; j >= c; j--) {
                    a[i][j] -= a[r][j] * a[i][c];
                }
            }
        }
    }
    if (r < n) {
        for (int i = r; i < n; i++) {
            if (fabs(a[i][n]) > eps) return 2;
        }
        return 1;
    }

    for (int i = n - 1; i >= 0; i--) {
        for (int j = i + 1; j < n; j++) {
            a[i][n] -= a[i][j] * a[j][n];
        }
    }

    return 0;
}

// 开关异或问题
int gauss()
{
    int r, c;
    for (r = 1, c = 1; c <= n; c ++ )
    {
        // 找主元
        int t = r;
        for (int i = r + 1; i <= n; i ++ )
            if (a[i][c])
                t = i;

        if (!a[t][c]) continue;
        // 交换
        for (int i = c; i <= n + 1; i ++ ) swap(a[t][i], a[r][i]);
        // 消
        for (int i = r + 1; i <= n; i ++ )
            for (int j = n + 1; j >= c; j -- )
                a[i][j] ^= a[i][c] & a[r][j];
        r ++ ;
    }

    int res = 1;
    if (r < n + 1)
    {
        for (int i = r; i <= n; i ++ )
        {
            if (a[i][n + 1]) return -1;  // 出现了 0 == !0,无解
            res *= 2;
        }
    }

    return res;
}

容斥原理

// 时间复杂度 O(2 ^ n)
#include <iostream>
#include <vector>
using ll = long long;
using namespace std;

vector<int> divisor;
void get_factor(int k)     // 对 k 进行分解质因数
{
    divisor.clear();
    for (int i = 2; i <= k / i; i ++)
    {
        if (k % i == 0)
        {
            divisor.push_back(i);
            while(k % i == 0) k /= i;
        }
    }
    if (k > 1) divisor.push_back(k);
}

int count(int limit)    
{
    int res = 0;
    for (int i = 1; i < (1 << divisor.size()); i ++)
    {
        ll x = 1, sum = 0;
        for (int j = 0; j < divisor.size(); j ++)
        {
            if ((i >> j) & 1) x *= divisor[j], sum ++;
        }
        if (sum & 1) res += limit / x;
        else res -= limit / x;
    }
    return limit - res;   // 利用容斥求出来的是 1 ~ limit中 与 k 不互质的个数, 返回互质的数量
}
// 同理利用 count  也可以求 l ~ r 区间内 与 某一数 x 互质的个数,或者不互质的个数
// 即  count(r) - count(l - 1) , 前缀和

博弈论

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

const int maxn = 110, maxm = 10010;

int n, m;
int s[maxn], f[maxm];

int sg(int x) {
    if (f[x] != -1) return f[x];
    unordered_set<int> S;
    for (int i = 0; i < m; i++) {
        int sum = s[i];
        if (x >= sum) S.insert(sg(x - sum));
    }
    for (int i = 0; ; i++) {
        if (!S.count(i)) {
            return f[x] = i;
        }
    }
}

int main() {
    cin >> m;
    for (int i = 0; i < m; i++) cin >> s[i];
    cin >> n;

    memset(f, -1, sizeof(f));
    int res = 0;
    for (int i = 0; i < n; i++) {
        int x;
        cin >> x;
        res ^= sg(x);
    }
    if (res) cout << "Yes" << endl;
    else cout << "No" << endl;
    return 0;
}

NTT

#include<iostream>
#include<cstring>
#include<vector>
#include<random>
using namespace std;
using LL = long long;
const int maxn = 2e6 + 5, mod = 998244353, G = 3, Gi = 332748118;
 
int qpow(int a, int b, int mod){
    int res = 1;
    while (b){
        if (b & 1) res = 1LL * res * a % mod;
        a = 1LL * a * a % mod;
        b >>= 1;
    }
    return res;
}
 
inline int mul(int a, int b){
    return 1LL * a * b % mod;
}
 
inline void add(int &a, int b){
    a += b;
    if (a >= mod) a -= mod;
}
 
inline void sub(int &a, int b){
    a -= b;
    if (a < 0) a += mod;
}
 
namespace NTT{
    vector<int> Omega(int L){
        int wn = qpow(G, mod / L, mod);
        vector<int> w(L); 
        w[L >> 1] = 1;
        for(int i = L / 2 + 1; i < L; i++) w[i] = mul(w[i - 1], wn);
        for(int i = L / 2 - 1; i >= 1; i--) w[i] = w[i << 1];
        return w;
    }
    auto W = Omega(1 << 21);
 
    void DIF(int *a, int n) {
        for(int k = n >> 1; k; k >>= 1)
            for(int i = 0, y; i < n; i += k << 1)
                for(int j = 0; j < k; j++){
                    y = a[i + j + k], a[i + j + k] = mul(a[i + j] - y + mod, W[k + j]), 
                    add(a[i + j], y);
                }
    }
 
    void IDIT(int *a, int n) {
        for (int k = 1; k < n; k <<= 1)
            for (int i = 0, x, y; i < n; i += k << 1)
                for(int j = 0; j < k; j++){
                    x = a[i + j], y = mul(a[i + j + k], W[k + j]),
                    a[i + j + k] = x - y < 0 ? x - y + mod : x - y;
                    add(a[i + j], y);
                }
        int inv = mod - (mod - 1) / n;
        for(int i = 0; i < n; i++) a[i] = mul(a[i], inv);
        reverse(a + 1, a + n);
    }
}
 
using Poly = std::vector<int>;
 
void DFT(Poly &a){
    NTT::DIF(a.data(), a.size());
}
 
void IDFT(Poly &a){
    NTT::IDIT(a.data(), a.size());
}
 
int norm(int n) { 
    return n == 1 ? 1 : (1 << (32 - __builtin_clz(n - 1))); 
}
 
void norm(Poly &a) { 
    if (!a.empty()) a.resize(norm(a.size()), 0); 
}
 
void dot(Poly &a, Poly &b) {
    for(int i = 0; i < a.size(); i++)
        a[i] = mul(a[i], b[i]);
}
 
Poly get(Poly A, Poly B){
    int n = A.size() + B.size() - 1, L = norm(n);
    A.resize(L), B.resize(L);
    vector<int> a(L), b(L), p(L);
    for(int i = 0; i < L; i++)
        a[i] = A[i] * A[i] * A[i], b[i] = B[i];
    DFT(a), DFT(b);
    dot(a, b);
    for(int i = 0; i < L; i++) add(p[i], a[i]);
    for(int i = 0; i < L; i++)
        a[i] = A[i], b[i] = B[i] * B[i] * B[i];
    DFT(a), DFT(b);
    dot(a, b);
    for(int i = 0; i < L; i++) add(p[i], a[i]);
    for(int i = 0; i < L; i++)
        a[i] = A[i] * A[i], b[i] = B[i] * B[i];
    DFT(a), DFT(b);
    dot(a, b);
    for(int i = 0; i < L; i++) sub(p[i], mul(a[i], 2));
    IDFT(p);
    p.resize(n);
    return p;
}
 
Poly operator*(Poly a, Poly b) {
    int n = a.size() + b.size() - 1, L = norm(n);
    if (a.size() <= 8 || b.size() <= 8) {
        Poly c(n);
        for(int i = 0; i < a.size(); i++)
            for(int j = 0; j < b.size(); j++)
                c[i + j] = (c[i + j] + 1LL * a[i] * b[j]) % mod;
        return c;
    }
    a.resize(L), b.resize(L);
    DFT(a), DFT(b), dot(a, b), IDFT(a);
    return a.resize(n), a;
}
// 分治FFT
Poly MulAll(int l, int r, vector<Poly> &a){
    if (l == r) return a[r];
    int mid = (l + r) / 2;
    return MulAll(l, mid, a) * MulAll(mid + 1, r, a);
}
/* 
	vector<Poly> a(n);
    for (int i = 0; i < n; i++) {
        a[i] = {1, d[i] - (i > 0)};
    }
    auto b = MulAll(0, n - 1, a);
    */

动态规划

背包

// 01 背包 (每件物品最多只能用一次)
#include <bits/stdc++.h>
using namespace std;

const int maxn = 1010;

int n, m;
int v[maxn], w[maxn];
int f[maxn];

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> v[i] >> w[i];

    for (int i = 1; i <= n; i++) {
        for (int j = m; j >= v[i]; j--) {
            f[j] = max(f[j], f[j - v[i]] + w[i]);
        }
    } 

    cout << f[m] << endl;
    return 0;
}

// 完全背包 (每件物品无限次) for (int j = v[i]; j <= m; j++)

// 整数划分 (计数dp)
    f[0] = 1;
    for (int i = 1; i <= n; i++) {
        for (int j = i; j <= n; j++) {
            f[j] = f[j] + f[j - i];
        }
    }
    cout << f[n] << endl;
 
// 多重背包 (每个物品最多用x[i]次)
// f[i][j] = max(f[i - 1][j - v[i] * k] + w[i] * k) (k = 0, 1, 2, ..., s[i])
// s[i]->logs[i] 二进制优化O(mnlog(s))

    int cnt = 0;
    for (int i = 1; i <= n; i++) {
        int a, b, s;
        cin >> a >> b >> s;
        int k = 1;
        while (k <= s) {
            cnt++;
            v[cnt] = a * k;
            w[cnt] = b * k;
            s -= k;
            k *= 2;
        }
        if (s > 0) {
            cnt++;
            v[cnt] = a * s;
            w[cnt] = b * s;
        }
    }
    n = cnt;
    for (int i = 1; i <= n; i++) {
        for (int j = m; j >= v[i]; j--) {
            f[j] = max(f[j], f[j - v[i]] + w[i]);
        }
    }
    cout << f[m] << endl;
    return 0;


// 分组背包 (n种每种有s[i]个, 1组最多1个)

int v[maxn][maxn], w[maxn][maxn];
int f[maxn], s[maxn];

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> s[i];
        for (int j = 0; j < s[i]; j++) {
            cin >> v[i][j] >> w[i][j];
        }
    }

    for (int i = 1; i <= n; i++) {
        for (int j = m; j >= 0; j--) {
            for (int k = 0; k < s[i]; k++) {
                if (v[i][k] <= j) {
                    f[j] = max(f[j], f[j - v[i][k]] + w[i][k]);
                }
            }
        }
    }

    cout << f[m] << endl;
}

数位DP

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

const int maxn = 35;
int f[maxn][10][110], mod;

void init() {
    memset(f, 0, sizeof(f));
    for (int i = 0; i < 10; i++) f[1][i][i % mod] = 1;

    for (int i = 2; i < maxn; i++) {
        for (int j = 0; j < 10; j++) {
            for (int k = 0; k < mod; k++) {
                for (int x = 0; x < 10; x++) {
                    f[i][j][k] += f[i - 1][x][((k - j) % mod + mod) % mod];
                }
            }
        }
    }
}

int dp(int n) {
    if (!n) return 1;
    vector<int> nums;
    while (n) nums.push_back(n % 10), n /= 10;
    int res = 0;
    int last = 0;
    for (int i = nums.size() - 1; i >= 0; i--) {
        int x = nums[i];
        for (int j = 0; j < x; j++) {
            res += f[i + 1][j][(-last % mod + mod) % mod];
        }
        last += x;
        if (!i && last % mod == 0) res++;
    }

    return res;
}

int main() {
    int l, r;
    while (cin >> l >> r >> mod) {
        init();
        cout << dp(r) - dp(l - 1) << endl;
    }
    return 0;
}

斜率优化DP

// k为和i有关, x和y和j有关
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const int maxn = 3e5 + 10;
ll f[maxn], a[maxn], b[maxn], q[maxn];

int main() {
    int n, s;
    cin >> n >> s;
    for (int i = 1; i <= n; i++) {
        cin >> a[i] >> b[i];
        a[i] += a[i - 1];
        b[i] += b[i - 1];
    }

    int hh = 0, tt = 0;
    for (int i = 1; i <= n; i++) {
        int l = hh, r = tt;
        while (l < r) {
            int mid = l + r >> 1;
            if (mid < tt && f[q[mid + 1]] - f[q[mid]] > (a[i] + s) * (b[q[mid + 1]] - b[q[mid]])) r = mid;
            else l = mid + 1;
        }
        f[i] = f[q[r]] + s * (b[n] - b[q[r]]) + a[i] * (b[i] - b[q[r]]);
        while (hh < tt && (double)(f[q[tt]] - f[q[tt - 1]]) * (b[i] - b[q[tt]]) >= (double)(f[i] - f[q[tt]]) * (b[q[tt]] - b[q[tt - 1]])) tt--;
        q[++tt] = i;
    }
    cout << f[n] << endl;
    return 0;
}

动态DP

​ 用线段树维护矩阵信息,支持单点修改和计算结果(query(1, l, r));

在这里插入图片描述

#include <iostream>
#include <cstring>
#include <iomanip>
#include <functional>
#include <cmath>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
using namespace std;
#define IOS                  \
ios::sync_with_stdio(false); \
cin.tie(0), cout.tie(0)
#define endl '\n'
using ll  = long long;
using PII = pair<int, int>;
using PLI = pair<ll, int>;

const int maxn = 2e5 + 5;
const int maxm = 2; 
const ll  INF = 1e18;
const int mod = 1e9 + 7;
int n, m, k;
int a[maxn];

struct matrix {
    ll v[maxm][maxm];
    matrix() {
        for (int i = 0; i < maxm; i++) {
            for (int j = 0; j < maxm; j++) {
                v[i][j] = INF;
            }
        }
    }
    matrix operator * (const matrix& oth) const {
        matrix res;
        for (int i = 0; i < maxm; i++) {
            for (int j = 0; j < maxm; j++) {
                for (int k = 0; k < maxm; k++) {
                    res.v[i][j] = min(res.v[i][j], v[i][k] + oth.v[k][j]);
                }
            }
        }
        return res; 
    }
};

struct Node {
    int l, r;
    matrix mt;
}tr[maxn * 4];

void pushup(matrix &root, matrix &l, matrix &r) {
    root = l * r;
}

void pushup(int u) {
    pushup(tr[u].mt, tr[u << 1].mt, tr[u << 1 | 1].mt);
}

void build(int u, int l, int r) {
    tr[u].l = l, tr[u].r = r;
    if (l == r) {
        auto &v = tr[u].mt.v;
        v[0][0] = v[0][1] = a[r];
        v[1][0] = 0;
        return;
    }
    int mid = l + r >> 1;
    build(u << 1, l, mid);
    build(u << 1 | 1, mid + 1, r);
    pushup(u);
}

void update(int u, int pos, int val) {
    if (tr[u].l == pos && tr[u].r == pos) {
        auto &v = tr[u].mt.v;
        v[0][0] = v[0][1] = val;
        return;
    }
    int mid = tr[u].l + tr[u].r >> 1;
    if (pos <= mid) update(u << 1, pos, val);
    else update(u << 1 | 1, pos, val);
    pushup(u); 
}

matrix query(int u, int l, int r) {
    if (l <= tr[u].l && tr[u].r <= r) return tr[u].mt;
    int mid = tr[u].l + tr[u].r >> 1;
    if (r <= mid) return query(u << 1, l, r);
    if (l > mid) return query(u << 1 | 1, l, r);
    matrix res;
    matrix lm = query(u << 1, l, r);
    matrix rm = query(u << 1 | 1, l, r);
    pushup(res, lm, rm);
    return res;
}

void Jared_McDs() {
    cin >> n;
    for (int i = 1; i < n; i++) cin >> a[i];
    build(1, 1, n - 1);
    int q;
    cin >> q;
    while (q--) {
        int pos, val;
        cin >> pos >> val;
        update(1, pos, val);
        cout << query(1, 1, n - 1).v[0][1] * 2ll << endl;
    }
}   
 
int main() {
#ifdef LOCAL
    freopen("in.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif
    IOS;
    int w_ = 1;
   //cin >> w_;
    while (w_--) {
        Jared_McDs();
    }
    return 0;
}

bitset优化

bitset优化要保证先找到拓扑序(排序)并且按拓扑序进行

最后更新答案要保证每条拓扑链的循环顺序大小要等价于逆拓扑序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gmc9QwFY-1689928960408)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20230614171957288.png)]

## CF 1826E
#include <bits/stdc++.h>
using namespace std;
#define IOS                  \
ios::sync_with_stdio(false); \
cin.tie(0), cout.tie(0)
#define endl '\n'
using ll  = long long;
using PII = pair<int, int>;
using PLI = pair<ll, int>;
 
const int maxn = 5010;
const int maxm = 2; 
const ll  INF = 1e18;
const int mod = 1e9 + 7;
int n, m, k, c;

void Jared_McDs() {
    cin >> m >> n;
    vector<ll> p(n);
    for (int i = 0; i < n; i++) cin >> p[i];
    vector<vector<int>> a(m, vector<int> (n));
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) cin >> a[i][j];
    }
    vector<int> index(n);
    for (int i = 0; i < n; i++) index[i] = i;

    vector<bitset<5000>> can(n);
    for (int i = 0; i < n; i++) can[i].set();
    for (int d = 0; d < m; d++) {
        sort(index.begin(), index.end(), [&](int j, int k) {
            return a[d][j] < a[d][k];
        });
        bitset<5000> tmp;
        for (int i = 0; i < n; ) {
            int j = i;
            while (j < n && a[d][index[j]] == a[d][index[i]]) {
                can[index[j]] &= tmp;
                j++;
            }
            while (i < j) {
                tmp[index[i]] = true;
                i++;
            }
        }
    }

    vector<ll> dp = p;
    for (int i : index) {
        for (int j = 0; j < n; j++) {
            if (can[i][j]) {
                dp[i] = max(dp[i], dp[j] + p[i]);
            }
        }   
    }
    cout << *max_element(dp.begin(), dp.end()) << endl;
}
 
int main() {
#ifdef LOCAL
    freopen("in.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif
    IOS;
    int w_ = 1;
   // cin >> w_;
    while (w_--) {
        Jared_McDs();
    }
    return 0;
}

状态压缩DP

一般是求最值,相同序列不同次序结果不同

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cWpEA7OP-1689928960409)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20230623002309990.png)]

#include <bits/stdc++.h>
using namespace std;
#define IOS                  \
ios::sync_with_stdio(false); \
cin.tie(0), cout.tie(0)
#define endl '\n'
using ll  = long long;
using ULL = unsigned long long;
using PII = pair<int, int>;
using PLI = pair<ll, int>;

const int maxn = 1010;
const int maxm = 1e6 + 10; 
const ll  INF = 1e18;
const int mod = 998244353;
const double eps = 1e-8;
int n, m, k, q;
int smin[25], sum[25], bitsum[1 << 21], dp[1 << 21];
unordered_map<int, int> mp[25], up[25];

void WTCh() {
    cin >> n;
    vector<string> s(n + 1);
    for (int i = 1; i <= n; i++) cin >> s[i], s[i] = '?' + s[i];
    for (int i = 1; i <= n; i++) {
        int val = 0;
        for (int j = 1; j < s[i].size(); j++) {
            val += (s[i][j] == '(' ? 1 : -1);
            smin[i] = min(smin[i], val);
            mp[i][val]++;
            if (mp[i][val] == 1) up[i][val] = mp[i][val + 1];
        }
        sum[i] = val;
    }
    for (int bit = 0; bit < 1 << n; bit++) {
        for (int i = 1; i <= n; i++) {
            if ((bit >> i - 1) & 1) bitsum[bit] += sum[i];
        }
    }
    vector<int> ok(1 << n, 0); // 是否有最小值小于0
    ok[0] = 1;
    int res = 0;
    for (int bit = 0; bit < 1 << n; bit++) {
        if (!ok[bit]) continue;
        for (int i = 1; i <= n; i++) {
            if (!((bit >> i - 1) & 1)) {
                int nxt = bit | (1 << i - 1);
                if (bitsum[bit] + smin[i] >= 0) dp[nxt] = max(dp[nxt], dp[bit] + mp[i][-bitsum[bit]]), ok[nxt] = 1;
                else res = max(res, dp[bit] + up[i][-bitsum[bit] - 1]);
            }
        }
    }
    for (int bit = 0; bit < 1 << n; bit++) res = max(res, dp[bit]);
    cout << res << endl;
}
  
int main() {
#ifdef LOCAL
    freopen("in.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif
    IOS;
    int w_ = 1;
    //cin >> w_;
    while (w_--) {
        WTCh();
    }
    return 0;
}

树形DP

按照以下方式解释合并:枚举已处理子级的所有子树内的顶点和新子级的子树内的顶部。迭代到子树的大小与遍历子树中的顶点的移动次数相同。合并将遍历所有的顶点对,使得该对的第一个顶点在第一集中,第二个顶点在第二集中。因此,树的每对顶点将被精确地处理一次(在这些顶点的lca中)

https://codeforces.com/contest/1499/problem/F

CF 1499 F

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
using ll  = long long;
using ULL = unsigned long long;
using PII = pair<ll, ll>;
using PLI = pair<ll, int>;

const int maxn = 6010;
const int maxm = 1e6 + 10; 
const ll  INF = 1e18;
const int mod = 998244353;
const double eps = 1e-8;
int n, m, k, q;
vector<int> g[maxn];
int f[maxn][maxn], tmp[maxn], dep[maxn], mxd[maxn];

void dfs(int u, int fa) {
    dep[u] = mxd[u] = dep[fa] + 1;
    f[u][0] = 1;
    for (auto v : g[u]) {
        if (v == fa) continue;
        dfs(v, u);
        int n = mxd[u] - dep[u], m = mxd[v] - dep[u];
        for (int i = 0; i < maxn; i++) tmp[i] = 0;
        for (int i = 0; i <= n; i++) {
            for (int j = 0; j <= m; j++) {
                tmp[i] = (tmp[i] + 1ll * f[u][i] * f[v][j] % mod) % mod;
                if (i + j + 1 <= k) tmp[max(i, j + 1)] = (tmp[max(i, j + 1)] + 1ll * f[u][i] * f[v][j] % mod) % mod;
            }
        }
        for (int i = 0; i <= max(n, m); i++) f[u][i] = tmp[i];
        mxd[u] = max(mxd[u], mxd[v]);
    }
}

void WTCh() {
    cin >> n >> k;
    for (int i = 1; i < n; i++) {
        int a, b;
        cin >> a >> b;
        g[a].push_back(b);
        g[b].push_back(a);
    }
    dfs(1, 0);
    ll res = 0;
    for (int i = 0; i <= k; i++) res = (res + f[1][i]) % mod;
    cout << res << endl;
}
  
int main() {
#ifdef LOCAL
    freopen("in.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int w_ = 1;
    //cin >> w_;
    while (w_--) {
        WTCh();
    }
    return 0;
}

字符串

AC自动机

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

int n, m;
const int maxn = 1e5 + 10;
int a[maxn], root[maxn], idx;
vector<int> nums;

int find(int x) {
    return lower_bound(nums.begin(), nums.end(), x) - nums.begin();
}

struct Node {
    int l, r;
    int cnt;
}tr[maxn * 4 + maxn * 17];

int build(int l, int r) {
    int p = idx++;
    if (l == r) {
        return p;
    }
    int mid = l + r >> 1;
    tr[p].l = build(l, mid);
    tr[p].r = build(mid + 1, r);
    return p;
}

int insert(int p, int l, int r, int x) {
    int q = idx++;
    tr[q] = tr[p];
    if (l == r) {
        tr[q].cnt++;
        return q;
    }

    int mid = l + r >> 1;
    if (x <= mid) tr[q].l = insert(tr[p].l, l, mid, x);
    else tr[q].r = insert(tr[p].r, mid + 1, r, x);
    tr[q].cnt = tr[tr[q].l].cnt + tr[tr[q].r].cnt;
    return q;
}

int query(int q, int p, int l, int r, int k) {
    if (l == r) return l;

    int cnt = tr[tr[q].l].cnt - tr[tr[p].l].cnt;
    int mid = l + r >> 1;
    if (k <= cnt) return query(tr[q].l, tr[p].l, l, mid, k);
    else return query(tr[q].r, tr[p].r, mid + 1, r, k - cnt);
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        nums.push_back(a[i]);
    }
    sort(nums.begin(), nums.end());
    nums.erase(unique(nums.begin(), nums.end()), nums.end());
    
    root[0] = build(0, nums.size() - 1);
    for (int i = 1; i <= n; i++) {
        root[i] = insert(root[i - 1], 0, nums.size() - 1, find(a[i]));
    }
    while (m--) {
        int l, r, k;
        cin >> l >> r >> k;
        cout << nums[query(root[r], root[l - 1], 0, nums.size() - 1, k)];
    }
    return 0;
}

后缀数组

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

const int maxn = 1e6 + 10;
int n, m;
char s[maxn];
int sa[maxn], x[maxn], y[maxn], c[maxn], rk[maxn], height[maxn];

void get_sa() {
    for (int i = 1; i <= n; i++) c[x[i] = s[i]]++;
    for (int i = 2; i <= m; i++) c[i] += c[i - 1];
    for (int i = n; i >= 1; i--) sa[c[x[i]]--] = i;
    for (int k = 1; k <= n; k <<= 1) {
        int num = 0;
        for (int i = n - k + 1; i <= n; i++) y[++num] = i;
        for (int i = 1; i <= n; i++) {
            if (sa[i] > k) y[++num] = sa[i] - k;
        }
        for (int i = 1; i <= m; i++) c[i] = 0;
        for (int i = 1; i <= n; i++) c[x[i]]++;
        for (int i = 2; i <= m; i++) c[i] += c[i - 1];
        for (int i = n; i >= 1; i--) sa[c[x[y[i]]]--] = y[i], y[i] = 0;
        swap(x, y);
        x[sa[1]] = 1, num = 1;
        for (int i = 2; i <= n; i++) {
            x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) ? num : ++num;
        }
        if (num == n) break;
        m = num;
    }
}

void get_height() {
    for (int i = 1; i <= n; i++) rk[sa[i]] = i;
    for (int i = 1, k = 0; i <= n; i++) {
        if (rk[i] == 1) continue;
        if (k) k--;
        int j = sa[rk[i] - 1];
        while (i + k <= n && j + k <= n && s[i + k] == s[j + k]) k++;
        height[rk[i]] = k;
    }
}

int main() {
    cin >> s + 1;
    n = strlen(s + 1), m = 122;
    get_sa();
    get_height();
    for (int i = 1; i <= n; i++) cout << sa[i] << " ";
    cout << endl;
    for (int i = 1; i <= n; i++) cout << height[i] << " ";
    cout << endl;
    return 0;
}

后缀自动机

一、SAM的性质:

SAM是个状态机。一个起点,若干终点。原串的所有子串和从SAM起点开始的所有路径一一对应,不重不漏。所以终点就是包含后缀的点。
每个点包含若干子串,每个子串都一一对应一条从起点到该点的路径。且这些子串一定是里面最长子串的连续后缀。
SAM问题中经常考虑两种边:
(1) 普通边,类似于Trie。表示在某个状态所表示的所有子串的后面添加一个字符。
(2) Link、Father。表示将某个状态所表示的最短子串的首字母删除。这类边构成一棵树。
二、SAM的构造思路

endpos(s):子串s所有出现的位置(尾字母下标)集合。SAM中的每个状态都一一对应一个endpos的等价类。
endpos的性质:
(1) 令 s1,s2 为 S 的两个子串 ,不妨设 |s1|≤|s2| (我们用 |s| 表示 s 的长度 ,此处等价于 s1 不长于 s2 )。则 s1 是 s2 的后缀当且仅当 endpos(s1)⊇endpos(s2) ,s1 不是 s2 的后缀当且仅当 endpos(s1)∩endpos(s2)=∅ 。
(2) 两个不同子串的endpos,要么有包含关系,要么没有交集。
(3) 两个子串的endpos相同,那么短串为长串的后缀。
(4) 对于一个状态 st ,以及任意的 longest(st) 的后缀 s ,如果 s 的长度满足:|shortest(st)|≤|s|≤|longsest(st)| ,那么 s∈substrings(st) 。

在这里插入图片描述

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
using ll = long long;

const int maxn = 2e6 + 10;

int tot = 1, last = 1;
struct Node {
    int len, fa;
    int ch[26];
}node[maxn];

char str[maxn];
ll f[maxn], res;
int h[maxn], e[maxn], ne[maxn], idx;

void extend(int c) {
    int p = last, np = last = ++tot;
    f[tot] = 1;
    node[np].len = node[p].len + 1;
    for (; p && !node[p].ch[c]; p = node[p].fa) node[p].ch[c] = np;
    if (!p) node[np].fa = 1;
    else {
        int q = node[p].ch[c];
        if (node[q].len == node[q].len + 1) node[np].fa = q;
        else {
            int nq = ++tot;
            node[nq] = node[q], node[nq].len = node[p].len + 1;
            node[q].fa = node[np].fa = nq;
            for (; p && node[p].ch[c] == q; p = node[p].fa) node[p].ch[c] = nq;
        }
    }
}

void add(int a, int b) {
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}

void dfs(int u) {
    for (int i = h[u]; ~i; i = ne[i]) {
        dfs(e[i]);
        f[u] += f[e[i]];
    }
    if (f[u] > 1) res = max(res, f[u] * node[u].len);
}

int main() {
    cin >> str;
    for (int i = 0; str[i]; i++) extend(str[i] - 'a');
    memset(h, -1, sizeof(h));
    for (int i = 2; i <= tot; i++) add(node[i].fa, i);
    dfs(1);
    cout << res << endl;
    return 0;
}

四维问题

#include <iostream>
#include <cstring>
#include <iomanip>
#include <functional>
#include <cmath>
#include <bitset>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
using namespace std;
#define IOS                  \
ios::sync_with_stdio(false); \
cin.tie(0), cout.tie(0)
#define endl '\n'
using ll  = long long;
using PII = pair<int, int>;
using PLI = pair<ll, int>;

const int maxn = 1e6 + 10;
const int maxm = 2; 
const ll  INF = 1e18;
const int mod = 1e9 + 7;
int n, m, k, q;
ll a[maxn], last[maxn];
ll res[maxn];

struct Segment_Tree {

​    void build(int u, int l, int r) {
​        tr[u].l = l, tr[u].r = r;
​        if (l == r) {
​            tr[u].sum = a[l];
​            // tr[u].maxv = a[l];
​            return;
​        }
​        int mid = l + r >> 1;
​        build(u << 1, l, mid);
​        build(u << 1 | 1, mid + 1, r);
​        pushup(u);
​    }

​    void pushup(int u) {
​        tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
​        //tr[u].maxv = max(tr[u << 1].maxv, tr[u << 1 | 1].maxv);
​    }

​    void update(int u, ll val) {
​        tr[u].sum += (tr[u].r - tr[u].l + 1) * val;
​        // tr[u].maxv += val;
​        tr[u].lazy += val;
​    }

​    void pushdown(int u) {
​        if (tr[u].lazy) {
​            update(u << 1, tr[u].lazy), update(u << 1 | 1, tr[u].lazy);
​            tr[u].lazy = 0;
​        }
​    }

​    void modify(int u, int l, int r, ll val) {
​        if (tr[u].l >= l && tr[u].r <= r) {
​            update(u, val);
​            return;
​        }
​        pushdown(u);
​        int mid = tr[u].l + tr[u].r >> 1;
​        if (l <= mid) modify(u << 1, l, r, val);
​        if (r > mid) modify(u << 1 | 1, l, r, val);
​        pushup(u);
​    }

​    ll query(int u, int l, int r) {
​        if (tr[u].l >= l && tr[u].r <= r) {
​            return tr[u].sum;
​        }
​        pushdown(u);
​        int mid = tr[u].l + tr[u].r >> 1;
​        ll res = 0;
​        if (l <= mid) res += query(u << 1, l, r);
​        if (r > mid) res += query(u << 1 | 1, l, r);
​        return res;
​    }

​    struct Node {
​        int l, r;
​        ll sum;
​        ll lazy;
​    }tr[maxn * 4];

}t1, t2;

struct Query {
    ll l, r, k, id;
};
vector<Query> b[maxn];

void add(int i, int l, int r, int x) {
    t1.modify(1, l, r, (ll)x * (n - i + 1));
    t2.modify(1, l, r, x);
}

ll query(int i, int l, int r) {
    return t1.query(1, l, r) - t2.query(1, l, r) * (n - i);
}


void Jared_McDs() {
    cin >> n >> q;
    t1.build(1, 1, n);
    t2.build(1, 1, n);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    for (int i = 1; i <= q; i++) {
        int l, r, x, y;
        cin >> l >> r >> x >> y;
        b[x - 1].push_back({l, r, -1, i});
        b[y].push_back({l, r, 1, i});
    }
    set<ll> st;
    st.insert(0);
    for (int i = 1; i <= n; i++) {
        add(i, i, i, i);
        st.insert(i);
        if (last[a[i]]) {
            auto pre = prev(st.find(last[a[i]])), nxt = next(st.find(last[a[i]]));
            ll l = *pre, x = last[a[i]], r = *nxt;
            add(i, l + 1, x, r - x);
            st.erase(last[a[i]]);
        }
        last[a[i]] = i;
        for (auto [l, r, k, id] : b[i]) {
            if (k < 0) res[id] -= query(i, l, r);
            else res[id] += query(i, l, r);
        }
    }
    for (int i = 1; i <= q; i++) cout << res[i] << endl;
}

int main() {
#ifdef LOCAL
    freopen("in.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif
    IOS;
    int w_ = 1;
   // cin >> w_;
    while (w_--) {
        Jared_McDs();
    }
    return 0;
}

高精度

using i128 = __int128;
 
const i128 inf = 1e36;
 
istream &operator>>(istream &is, i128 &n) {
    n = 0;
    string s;
    is >> s;
    for (auto c : s) {
        n = 10 * n + c - '0';
    }
    return is;
}
 
ostream &operator<<(ostream &os, i128 n) {
    if (!n) return os << 0;
    string s;
    while (n) {
        s += '0' + n % 10;
        n /= 10;
    }
    reverse(s.begin(), s.end());
    return os << s;
}

ModInt

template<const int T>
struct ModInt {
    const static int mod = T;
    int x;
    ModInt(int x = 0) : x(x % mod) {}
    ModInt(long long x) : x(int(x % mod)) {} 
    int val() { return x; }
    ModInt operator + (const ModInt &a) const { int x0 = x + a.x; return ModInt(x0 < mod ? x0 : x0 - mod); }
    ModInt operator - (const ModInt &a) const { int x0 = x - a.x; return ModInt(x0 < 0 ? x0 + mod : x0); }
    ModInt operator * (const ModInt &a) const { return ModInt(1LL * x * a.x % mod); }
    ModInt operator / (const ModInt &a) const { return *this * a.inv(); }
    bool operator == (const ModInt &a) const { return x == a.x; };
    bool operator != (const ModInt &a) const { return x != a.x; };
    void operator += (const ModInt &a) { x += a.x; if (x >= mod) x -= mod; }
    void operator -= (const ModInt &a) { x -= a.x; if (x < 0) x += mod; }
    void operator *= (const ModInt &a) { x = 1LL * x * a.x % mod; }
    void operator /= (const ModInt &a) { *this = *this / a; }
    friend ModInt operator + (int y, const ModInt &a){ int x0 = y + a.x; return ModInt(x0 < mod ? x0 : x0 - mod); }
    friend ModInt operator - (int y, const ModInt &a){ int x0 = y - a.x; return ModInt(x0 < 0 ? x0 + mod : x0); }
    friend ModInt operator * (int y, const ModInt &a){ return ModInt(1LL * y * a.x % mod);}
    friend ModInt operator / (int y, const ModInt &a){ return ModInt(y) / a;}
    friend ostream &operator<<(ostream &os, const ModInt &a) { return os << a.x;}
    friend istream &operator>>(istream &is, ModInt &t){return is >> t.x;}

    ModInt pow(int64_t n) const {
        ModInt res(1), mul(x);
        while(n){
            if (n & 1) res *= mul;
            mul *= mul;
            n >>= 1;
        }
        return res;
    }
    
    ModInt inv() const {
        int a = x, b = mod, u = 1, v = 0;
        while (b) {
            int t = a / b;
            a -= t * b; swap(a, b);
            u -= t * v; swap(u, v);
        }
        if (u < 0) u += mod;
        return u;
    }
    
};
using mint = ModInt<1000000007>;

mint inv[maxn]; // 某个数逆元
void init(){
    inv[1] = 1;
    for(int i = 2; i < maxn; i++)
        inv[i] = mint(mod - mod / i) * inv[mod % i];
}

随机数

mt19937_64 rnd(random_device{}());
uniform_int_distribution<ll> dist(0, LLONG_MAX);  

for (int i = 1; i <= m; i++) {
        int l, r;
        cin >> l >> r;
        ll h = dist(rnd);
        d[l] += h;
        if (r + 1 <= n) d[r + 1] -= h;
}

子集前缀和

和前面是一样的,只不过按集合元素拆分维度。

有一个集合 N N N,元素个数为n个,对其中任意一个子集 U U U,赋予一个值 A U A_U AU, 现在要求 N N N的所有子集 S S S各自的前缀和,即求 B S B_S BS满足

B S = ∑ U ⊂ S A U B_{S} = \sum_{U \subset S} A_U BS=USAU

如果直接对每个子集,枚举其子集求解,复杂度可达 O ( ∑ i = 0 n ( n i ) 2 i ) = O ( ( 1 + 2 ) n ) = O ( 3 n ) \mathcal{O}(\sum_{i = 0}^{n} \binom{n}{i} 2^i ) =\mathcal{O}((1+2)^n) = \mathcal{O}(3^n) O(i=0n(in)2i)=O((1+2)n)=O(3n)

我们可以按元素拆分为n维,每个维度的值有两个,0和1。用下标表示法可以表示法就是i和j只有0,1两种取值。

按照集合表示就是,设 s ∈ S s\in S sS S S S的一个元素有

B S = ∑ U ⊂ S A U = ∑ s ∈ V ∧ V ⊂ S A V + ∑ s ∉ V ∧ V ⊂ S A V B_S = \sum_{U\subset S} A_U = \sum_{s \in V \wedge V\subset S } A_{V } + \sum_{s \notin V \wedge V\subset S } A_{V } BS=USAU=sVVSAV+s/VVSAV

同理,我们可以得到如下代码

for (int i = 0; i < n; ++i) {
    for (int j = 1; j < 1 << n; ++j) {
      if (j & (1 << i)) {
        a[j] += a[j ^ (1 << i)];
      }
    }
  }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值