算法竞赛常用模板库

观前须知

Sugar_Cube的博客园主页

声明

本文使用 CC BY-NC-SA 4.0 许可

本文包含了笔者常用的 OI 算法、数据结构的模板。
不保证算法最优,但能通过相应的模板题(如果有会挂出)。
如有错误请在评论区指出(虽然大抵没人看就是了)。
码风是笔者的个人习惯(能看懂就好喵)。
代码会省略快读 Read()
持续更新
咕咕咕

输入输出优化

快读

inline int Read() {
        int res = 0;
        bool flag = false;
        int c = getchar();
        //~c防止EOF读到-1而卡死循环,一般情况可省略
        while ((c < '0' || c > '9') && ~c) {
            flag |= c == '-';
            c = getchar();
        }
        while (c >= '0' && c <= '9') {
            res = (res << 1) + (res << 3) + (c ^ 48);
            c = getchar();
        }
        return flag ? -res : res;
}

浮点数快读

typedef long double ld;

inline ld Read() {
    ld res = 0;
    bool flag = false;
    int c = getchar();
    while ((c < '0' || c > '9') && ~c) {
        flag |= c == '-';
        c = getchar();
    }
    while (c >= '0' && c <= '9') {
        res = res * 10 + (c ^ 48);
        c = getchar();
    }
    if (c == '.') {
        c = getchar();
        ld f = 1;
        while (c >= '0' && c <= '9') {
            f /= 10;
            res += f * (c ^ 48);
            c = getchar();
        }
    }
    return flag ? -res : res;
}

数据结构

树状数组

Luogu P3374 【模板】树状数组 1

constexpr int AwA = 5e5 + 10;

int n, m;
int a[AwA];
int c[AwA];

inline void Update(int idx, int x) {
    while (idx <= n) {
        c[idx] += x;
        idx += idx & -idx;
    }
}

inline int Query(int idx) {
    int res = 0;
    while (idx) {
        res += c[idx];
        idx -= idx & -idx;
    }
    return res;
}

int main() {
    n = Read(), m = Read();
    //根据树状数组定义O(n)建树
    for (int i = 1; i <= n; i++) a[i] = a[i - 1] + Read();
    for (int i = 1; i <= n; i++) c[i] = a[i] - a[i - (i & -i)];

    while (m--) {
        int op = Read(), x = Read(), y = Read();
        if (op == 1) Update(x, y);
        else printf("%d\n", Query(y) - Query(x - 1));
    }

    return 0;
}

ST表

Luogu P3865 【模板】ST表

constexpr int AwA = 1e5 + 10;
constexpr int QwQ = 21;

int n, m;
int f[AwA][QwQ], a[AwA];

inline void PreST() {
    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]);
}

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

int main() {
    n = Read(), m = Read();
    for (int i = 1; i <= n; i++) a[i] = Read();
    PreST();
    while (m--) {
        int l = Read(), r = Read();
        printf("%d\n", Query(l, r));
    }
    return 0;
}

线段树

Luogu P3373 【模板】线段树2

#include<bits/stdc++.h>

using namespace std;

inline static int Read() {
    int res = 0;
    bool flag = false;
    int c = getchar();
    while ((c < '0' || c > '9') && ~c) {
        flag |= c == '-';
        c = getchar();
    }
    while (c >= '0' && c <= '9') {
        res = (res << 1) + (res << 3) + (c ^ 48);
        c = getchar();
    }
    return flag ? -res : res;
}

static constexpr int AwA = 1e5 + 10;

int n, Mod, Q;
int a[AwA];
//不动态开点的线段树要开4倍数组
struct Node {
    int mul_tag, add_tag;
    int sum;
} tr[AwA << 2];

inline void PushUp(int u) {
    //线段树的左右儿子分别为2*u,2*u+1
    tr[u].sum = (tr[u << 1].sum + tr[u << 1 | 1].sum) % Mod;
}

//本棵线段树最重要的一个函数
inline void PushDown(int u, int l, int r) {
    int lc = u << 1, rc = u << 1 | 1, mid = (l + r) >> 1;
    if (tr[u].mul_tag != 1) {
        tr[lc].mul_tag = int(1ll * tr[lc].mul_tag * tr[u].mul_tag % Mod);
        tr[lc].add_tag = int(1ll * tr[lc].add_tag * tr[u].mul_tag % Mod);
        tr[lc].sum = int(1ll * tr[lc].sum * tr[u].mul_tag % Mod);
        tr[rc].mul_tag = int(1ll * tr[rc].mul_tag * tr[u].mul_tag % Mod);
        tr[rc].add_tag = int(1ll * tr[rc].add_tag * tr[u].mul_tag % Mod);
        tr[rc].sum = int(1ll * tr[rc].sum * tr[u].mul_tag % Mod);
        tr[u].mul_tag = 1;
    }
    if (tr[u].add_tag) {
        tr[lc].add_tag = (tr[lc].add_tag + tr[u].add_tag) % Mod;
        tr[lc].sum = int((tr[lc].sum + 1ll * tr[u].add_tag * (mid - l + 1)) % Mod);
        tr[rc].add_tag = (tr[rc].add_tag + tr[u].add_tag) % Mod;
        tr[rc].sum = int((tr[rc].sum + 1ll * tr[u].add_tag * (r - mid)) % Mod);
        tr[u].add_tag = 0;
    }
}

//O(n)建树
void Build(int u, int l, int r) {
    tr[u] = {1, 0, 0};
    if (l == r) return void(tr[u].sum = a[l]);
    int mid = (l + r) >> 1;
    Build(u << 1, l, mid);
    Build(u << 1 | 1, mid + 1, r);
    PushUp(u);
}

void UpdateMul(int u, int l, int r, int lx, int rx, int val) {
    if (lx <= l && r <= rx) {
        tr[u].sum = int(1ll * tr[u].sum * val % Mod);
        tr[u].mul_tag = int(1ll * tr[u].mul_tag * val % Mod);
        tr[u].add_tag = int(1ll * tr[u].add_tag * val % Mod);
        return;
    }
    PushDown(u, l, r);
    int mid = (l + r) >> 1;
    if (lx <= mid) UpdateMul(u << 1, l, mid, lx, rx, val);
    if (mid + 1 <= rx) UpdateMul(u << 1 | 1, mid + 1, r, lx, rx, val);
    PushUp(u);
}

void UpdateAdd(int u, int l, int r, int lx, int rx, int val) {
    if (lx <= l && r <= rx) {
        tr[u].sum = int((1ll * val * (r - l + 1) + tr[u].sum) % Mod);
        tr[u].add_tag = (tr[u].add_tag + val) % Mod;
        return;
    }
    PushDown(u, l, r);
    int mid = (l + r) >> 1;
    if (lx <= mid) UpdateAdd(u << 1, l, mid, lx, rx, val);
    if (mid + 1 <= rx) UpdateAdd(u << 1 | 1, mid + 1, r, lx, rx, val);
    PushUp(u);
}

int Query(int u, int l, int r, int lx, int rx) {
    if (lx <= l && r <= rx) return tr[u].sum;
    PushDown(u, l, r);
    int mid = (l + r) >> 1, res = 0;
    if (lx <= mid) res = (res + Query(u << 1, l, mid, lx, rx)) % Mod;
    if (mid + 1 <= rx) res = (res + Query(u << 1 | 1, mid + 1, r, lx, rx)) % Mod;
    return res;
}

int main() {
    n = Read(), Q = Read(), Mod = Read();
    for (int i = 1; i <= n; i++) a[i] = Read();
    Build(1, 1, n);
    while (Q--) {
        int op = Read();
        if (op == 1) {
            int l = Read(), r = Read(), val = Read();
            UpdateMul(1, 1, n, l, r, val % Mod);
        } else if (op == 2) {
            int l = Read(), r = Read(), val = Read();
            UpdateAdd(1, 1, n, l, r, val % Mod);
        } else {
            int l = Read(), r = Read();
            printf("%d\n", Query(1, 1, n, l, r));
        }
    }
    return 0;
}

主席树

P3834 【模板】可持久化线段树 2

constexpr int AwA = 2e5 + 10;
//空间复杂度nlogK尽量开大点
constexpr int PwP = 5e6 + 10;

struct Node {
    int lc, rc, val;
} tr[PwP];

int n, m, K, tot;
int a[AwA], b[AwA];
int rt[AwA];

void Update(int &u, int nu, int l, int r, int pos) {
    tr[u = ++tot] = tr[nu];
    tr[u].val++;
    if (l == r) return;
    int mid = (l + r) >> 1;
    if (pos <= mid) Update(tr[u].lc, tr[nu].lc, l, mid, pos);
    else Update(tr[u].rc, tr[nu].rc, mid + 1, r, pos);
}

//类似前缀和的查询
int Query(int lu, int ru, int l, int r, int rk) {
    if (l == r) return l;
    return rk <= tr[tr[ru].lc].val - tr[tr[lu].lc].val ?
           Query(tr[lu].lc, tr[ru].lc, l, (l + r) >> 1, rk) :
           Query(tr[lu].rc, tr[ru].rc, ((l + r) >> 1) + 1, r, rk - tr[tr[ru].lc].val + tr[tr[lu].lc].val);
}

int main() {
    n = Read(), m = Read();
    for (int i = 1; i <= n; i++) b[i] = a[i] = Read();
    //离散化
    sort(b + 1, b + n + 1);
    K = int(unique(b + 1, b + n + 1) - b - 1);
    for (int i = 1; i <= n; i++) a[i] = int(lower_bound(b + 1, b + K + 1, a[i]) - b);

    for (int i = 1; i <= n; i++) Update(rt[i], rt[i - 1], 1, K, a[i]);

    int l, r, rk;
    while (m--) {
        l = Read(), r = Read(), rk = Read();
        printf("%d\n", b[Query(rt[l - 1], rt[r], 1, K, rk)]);
    }
    return 0;
}

FHQ Treap

Luogu P3369 普通平衡树

constexpr int AwA = 1e6 + 10;

//生成随机数
mt19937 rd{random_device()()};

struct Node {
    int lc, rc, sz;
    int val;
    //mt19937 返回uint类型
    unsigned int rd;
} tr[AwA];

int rt, tot;

inline int NewNode(int _val) {
    tr[++tot] = {0, 0, 1, _val, rd()};
    return tot;
}

inline void PushUp(int u) {
    tr[u].sz = tr[tr[u].lc].sz + tr[tr[u].rc].sz + 1;
}

void SplitVal(int u, int val, int &x, int &y) {
    //压行.jpg
    if (!u) return void(x = y = 0);
    if (tr[u].val <= val) {
        x = u;
        SplitVal(tr[u].rc, val, tr[x].rc, y);
    } else {
        y = u;
        SplitVal(tr[u].lc, val, x, tr[y].lc);
    }
    PushUp(u);
}

int Merge(int x, int y) {
    if (!x || !y) return x | y;
    if (tr[x].rd <= tr[y].rd) {
        tr[x].rc = Merge(tr[x].rc, y);
        PushUp(x);
        return x;
    }
    tr[y].lc = Merge(x, tr[y].lc);
    PushUp(y);
    return y;
}

inline void Insert(int _val) {
    int x, y;
    SplitVal(rt, _val, x, y);
    rt = Merge(Merge(x, NewNode(_val)), y);
}

int main() {
    int Q = Read();
    while (Q--) {
        int op = Read(), v = Read();
        if (op == 1) Insert(v);
        else if (op == 2) {
            int x, y, z;
            SplitVal(rt, v, x, z);
            SplitVal(x, v - 1, x, y);
            y = Merge(tr[y].lc, tr[y].rc);
            rt = Merge(Merge(x, y), z);
        } else if (op == 3) {
            int x, y;
            SplitVal(rt, v - 1, x, y);
            printf("%d\n", tr[x].sz + 1);
            rt = Merge(x, y);
        } else if (op == 4) {
            int u = rt;
            while (true) {
                int sz = tr[tr[u].lc].sz + 1;
                if (sz == v) break;
                else if (sz > v) u = tr[u].lc;
                else u = tr[u].rc, v -= sz;
            }
            printf("%d\n", tr[u].val);
        } else if (op == 5) {
            int x, y;
            SplitVal(rt, v - 1, x, y);
            int u = x;
            while (tr[u].rc) u = tr[u].rc;
            rt = Merge(x, y);
            printf("%d\n", tr[u].val);
        } else {
            int x, y;
            SplitVal(rt, v, x, y);
            int u = y;
            while (tr[u].lc) u = tr[u].lc;
            rt = Merge(x, y);
            printf("%d\n", tr[u].val);
        }
    }
    return 0;
}

FHQ Treap 文艺平衡树

Luogu P3391 文艺平衡树

constexpr int AwA = 1e5 + 10;

mt19937 rd{random_device()()};

struct Node {
    int lc, rc, sz;
    int val;
    unsigned int rd;
    bool revTag;
} tr[AwA];

int rt, tot;

inline int NewNode(int _val) {
    tr[++tot] = {0, 0, 1, _val, rd(), false};
    return tot;
}

inline void PushUp(int u) {
    tr[u].sz = tr[tr[u].lc].sz + tr[tr[u].rc].sz + 1;
}

inline void PushDown(int u) {
    if (!tr[u].revTag) return;
    swap(tr[u].lc, tr[u].rc);
    if (tr[u].lc) tr[tr[u].lc].revTag ^= 1;
    if (tr[u].rc) tr[tr[u].rc].revTag ^= 1;
    tr[u].revTag = false;
}

void SplitSz(int u, int sz, int &x, int &y) {
    if (!u) return void(x = y = 0);
    PushDown(u);
    if (tr[tr[u].lc].sz + 1 <= sz) {
        x = u;
        SplitSz(tr[u].rc, sz - tr[tr[u].lc].sz - 1, tr[x].rc, y);
    } else {
        y = u;
        SplitSz(tr[u].lc, sz, x, tr[y].lc);
    }
    PushUp(u);
}

int Merge(int x, int y) {
    if (!x || !y) return x | y;
    if (tr[x].rd <= tr[y].rd) {
        PushDown(x);
        tr[x].rc = Merge(tr[x].rc, y);
        PushUp(x);
        return x;
    }
    PushDown(y);
    tr[y].lc = Merge(x, tr[y].lc);
    PushUp(y);
    return y;
}

inline void Build(int n) {
    for (int i = 1; i <= n; i++) NewNode(i);
    static int stk[AwA], top;
    stk[top = 1] = 1;
    for (int i = 2; i <= n; i++) {
        int tmp = 0;
        while (top && tr[stk[top]].rd > tr[i].rd) PushUp(tmp = stk[top--]);
        if (top) tr[stk[top]].rc = i;
        if (tmp) tr[i].lc = tmp;
        stk[++top] = i;
    }
    for (int i = top; i; i--) PushUp(i);
    rt = stk[1];
}

void Print(int u) {
    if (!u) return;
    PushDown(u);
    Print(tr[u].lc);
    printf("%d ", tr[u].val);
    Print(tr[u].rc);
}

int main() {
    int n = Read(), Q = Read();
    Build(n);
    while (Q--) {
        int l = Read(), r = Read();
        int x, y, z;
        SplitSz(rt, r, x, z);
        SplitSz(x, l - 1, x, y);
        tr[y].revTag ^= 1;
        rt = Merge(Merge(x, y), z);
    }
    Print(rt);
    putchar('\n');
    return 0;
}

块状链表

因为笔者暂时没有POJ账号所以模板题先咕咕咕喵

static constexpr int QwQ = 1e3;

struct Node {
    int nxt, sz;
    int a[(QwQ << 1) + 10];

    inline void Insert(int pos, int val) {
        for (int i = sz; i >= pos; i--) a[i + 1] = a[i];
        a[pos] = val;
        sz++;
    }

    inline void Delete(int pos) {
        for (int i = pos; i < sz; i++) a[i] = a[i + 1];
        sz--;
    }
} t[QwQ + 10];

int tcnt = 1;

inline void Check(int u) {
    if (t[u].sz < QwQ << 1) return;
    int v = ++tcnt;
    t[v].nxt = t[u].nxt;
    t[u].nxt = v;
    t[v].sz = t[u].sz - QwQ;
    for (int i = 1; i <= t[v].sz; i++) t[v].a[i] = t[u].a[i + QwQ];
    t[u].sz = QwQ;
}

inline void Insert(int pos, int val) {
    int u;
    for (u = 1; t[u].nxt && t[u].sz < pos; pos -= t[u].sz, u = t[u].nxt);
    t[u].Insert(min(pos, t[u].sz + 1), val);
    Check(u);
}

inline int Query(int pos) {
    int u;
    for (u = 1; t[u].nxt && t[u].sz < pos; pos -= t[u].sz, u = t[u].nxt);
    return t[u].a[pos];
}

inline void Delete(int pos) {
    int u;
    for (u = 1; t[u].nxt && t[u].sz < pos; pos -= t[u].sz, u = t[u].nxt);
    t[u].Delete(pos);
}

图论

Dijkstra

Luogu P4779 【模板】单源最短路径(标准版)

constexpr int AwA = 1e5 + 10;
constexpr int QwQ = 2e5 + 10;

struct Edge {
    int nxt, v, w;
} e[QwQ];
int head[AwA], ecnt;

inline void AddEdge(int w, int v, int u) {
    e[++ecnt] = {head[u], v, w};
    head[u] = ecnt;
}

int n, m, S;
int dis[AwA];
bool vis[AwA];

inline void Dijkstra() {
    priority_queue<pair<int, int>> q;
    memset(dis + 1, 0x3f, sizeof(int) * n);
    memset(vis + 1, 0, sizeof(bool) * n);
    dis[S] = 0;
    q.emplace(0, S);
    while (!q.empty()) {
        int u = q.top().second;
        q.pop();
        if (vis[u]) continue;
        vis[u] = true;
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].v;
            if (dis[u] + e[i].w < dis[v]) {
                dis[v] = dis[u] + e[i].w;
                q.emplace(-dis[v], v);
            }
        }
    }
}

int main() {
    n = Read(), m = Read(), S = Read();
    for (int i = 1; i <= m; i++) AddEdge(Read(), Read(), Read());
    Dijkstra();
    for (int i = 1; i <= n; i++) printf("%d ", dis[i]);
    putchar('\n');
    return 0;
}

SPFA

Luogu P3371 【模板】单源最短路径(弱化版)

笔者这里写了个SLF-swap优化喵

constexpr int AwA = 1e4 + 10;
constexpr int QwQ = 5e5 + 10;

struct Edge {
    int nxt, v, w;
} e[QwQ];
int head[AwA], ecnt;

inline void AddEdge(int w, int v, int u) { e[++ecnt] = {head[u], v, w}, head[u] = ecnt; }

int n, m, S;
int q[AwA], ql, qr;
int dis[AwA];
bool inq[AwA];

void Spfa() {
    for (int i = 1; i <= n; i++) dis[i] = INT_MAX;
    memset(inq + 1, 0, sizeof(bool) * n);
    dis[S] = 0;
    ql = qr = 1;
    q[qr] = S;
    inq[S] = true;
    //手写循环队列
    auto dq = [&](int x) -> int & { return q[(x - 1) % n + 1]; };
    while (ql <= qr) {
        int u = dq(ql++);
        //SLF-swap优化
        if (dis[dq(ql)] > dis[dq(qr)]) swap(dq(ql), dq(qr));
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].v;
            if (dis[v] > dis[u] + e[i].w) {
                dis[v] = dis[u] + e[i].w;
                if (!inq[v]) {
                    dq(++qr) = v;
                    inq[v] = true;
                    if (dis[dq(ql)] > dis[dq(qr)]) swap(dq(ql), dq(qr));
                }
            }
        }
        inq[u] = false;
    }
}

int main() {
    n = Read(), m = Read(), S = Read();
    while (m--) AddEdge(Read(), Read(), Read());
    Spfa();
    for (int i = 1; i <= n; i++) printf("%d ", dis[i]);
    putchar('\n');
    return 0;
}

SPFA判负环

Luogu P3385 【模板】负环

constexpr int AwA = 2e3 + 10;
constexpr int QwQ = 6e3 + 10;

struct Edge {
    int nxt, v, w;
} e[QwQ];
int head[AwA], ecnt;

inline void AddEdge(int w, int v, int u) { e[++ecnt] = {head[u], v, w}, head[u] = ecnt; }

int n, m;
int q[AwA], ql, qr;
int dis[AwA], cnt[AwA];
bool inq[AwA];

bool Spfa() {
    memset(dis + 1, 0x3f, sizeof(int) * n);
    memset(inq + 1, 0, sizeof(bool) * n);
    dis[1] = cnt[1] = 0;
    ql = qr = 1;
    q[qr] = 1;
    inq[1] = true;
    auto dq = [&](int x) -> int & { return q[(x - 1) % n + 1]; };
    while (ql <= qr) {
        int u = dq(ql++);
        if (dis[dq(ql)] > dis[dq(qr)]) swap(dq(ql), dq(qr));
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].v;
            if (dis[v] > dis[u] + e[i].w) {
                dis[v] = dis[u] + e[i].w;
                cnt[v] = cnt[u] + 1;
                if (cnt[v] >= n) return true;
                if (!inq[v]) {
                    dq(++qr) = v;
                    inq[v] = true;
                    if (dis[dq(ql)] > dis[dq(qr)]) swap(dq(ql), dq(qr));
                }
            }
        }
        inq[u] = false;
    }
    return false;
}

int main() {
    int T = Read();
    while (T--) {
        n = Read(), m = Read();

        memset(head + 1, 0, sizeof(int) * n);
        ecnt = 0;

        while (m--) {
            int u = Read(), v = Read(), w = Read();
            AddEdge(w, v, u);
            if (w >= 0) AddEdge(w, u, v);
        }
        puts(Spfa() ? "YES" : "NO");
    }
    return 0;
}

差分约束

Luogu P5960 【模板】差分约束

constexpr int AwA = 5e3 + 10;
constexpr int QwQ = 1e4 + 10;

struct Edge {
    int nxt, v, w;
} e[QwQ];
int head[AwA], ecnt;

inline void AddEdge(int w, int u, int v) { e[++ecnt] = {head[u], v, w}, head[u] = ecnt; }

int n, m;
int q[AwA], ql, qr;
int dis[AwA], cnt[AwA];
bool inq[AwA];

bool Spfa() {
    memset(dis + 1, 0x3f, sizeof(int) * n);
    memset(inq + 1, 0, sizeof(bool) * n);
    dis[n + 1] = cnt[n + 1] = 0;
    ql = qr = 1;
    q[qr] = n + 1;
    inq[n + 1] = true;
    auto dq = [&](int x) -> int & { return q[(x - 1) % (n + 1) + 1]; };
    while (ql <= qr) {
        int u = dq(ql++);
        if (dis[dq(ql)] > dis[dq(qr)]) swap(dq(ql), dq(qr));
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].v;
            if (dis[v] > dis[u] + e[i].w) {
                dis[v] = dis[u] + e[i].w;
                cnt[v] = cnt[u] + 1;
                if (cnt[v] >= n + 1) return true;
                if (!inq[v]) {
                    dq(++qr) = v;
                    inq[v] = true;
                    if (dis[dq(ql)] > dis[dq(qr)]) swap(dq(ql), dq(qr));
                }
            }
        }
        inq[u] = false;
    }
    return false;
}

int main() {
    n = Read(), m = Read();
    while (m--) AddEdge(Read(), Read(), Read());
    for (int i = 1; i <= n; i++) AddEdge(0, n + 1, i);

    //SPFA部分基本同上面判负环那个
    if (Spfa()) puts("NO");
    else {
        for (int i = 1; i <= n; i++) printf("%d ", dis[i]);
        putchar('\n');
    }
    return 0;
}

Kruskal

Luogu P3366 【模板】最小生成树

constexpr int AwA = 5e3 + 10;
constexpr int QwQ = 2e5 + 10;

struct {
    int u, v, w;
} e[QwQ];

int n, m;
int fa[AwA];

int Find(int x) { return fa[x] ? fa[x] = Find(fa[x]) : x; }

inline int Kruskal() {
    sort(e + 1, e + m + 1, [](auto &e1, auto &e2) { return e1.w < e2.w; });
    int cnt = 0, ans = 0;
    for (int i = 1; i <= m && cnt < n - 1; i++) {
        int x = Find(e[i].u), y = Find(e[i].v);
        if (x == y) continue;
        fa[x] = y;
        ans += e[i].w;
        cnt++;
    }
    return cnt == n - 1 ? ans : -1;
}

int main() {
    n = Read(), m = Read();
    for (int i = 1; i <= m; i++) e[i] = {Read(), Read(), Read()};
    int res = Kruskal();
    ~res ? printf("%d\n", res) : puts("orz");
    return 0;
}

Tarjan 求强连通分量

木得模板题抱歉喵~

int dfn[AwA], low[AwA], co[AwA], sz[AwA];
int stk[AwA];

void Tarjan(int u) {
    dfn[u] = low[u] = ++dfn[0];
    stk[++stk[0]] = u;
    for (int i = head[u]; i; i = e[i].nxt) {
        int v = e[i].v;
        if (!dfn[v]) {
            Tarjan(v);
            low[u] = min(low[u], low[v]);
        } else if (!co[v]) low[u] = min(low[u], dfn[v]);
    }
    if (dfn[u] == low[u]) {
        co[u] = ++co[0];
        sz[co[0]] = 1;
        while (stk[stk[0]] != u) {
            co[stk[stk[0]--]] = co[0];
            sz[co[0]]++;
        }
        stk[0]--;
    }
}

Tarjan 求割点

P3388 【模板】割点(割顶)

constexpr int AwA = 2e4 + 10;
constexpr int QwQ = 1e5 + 10;

struct Edge {
    int nxt, v;
} e[QwQ << 1];
int head[AwA], ecnt = 1;

inline void AddEdge(int u, int v) {
    e[++ecnt] = {head[u], v}, head[u] = ecnt;
    e[++ecnt] = {head[v], u}, head[v] = ecnt;
}

int n, m;
int dfn[AwA], low[AwA];
bool ans[AwA];

void Tarjan(int u, int ew = 0) {
    dfn[u] = low[u] = ++dfn[0];
    int cnt = 0;
    for (int i = head[u]; i; i = e[i].nxt) {
        if ((ew ^ 1) == i) continue;
        int v = e[i].v;
        if (!dfn[v]) {
            cnt++;
            Tarjan(v, i);
            low[u] = min(low[u], low[v]);
            if (ew && low[v] >= dfn[u]) ans[u] = true;
        } else low[u] = min(low[u], dfn[v]);
    }
    if (!ew && cnt >= 2) ans[u] = true;
}

int main() {
    n = Read(), m = Read();
    for (int i = 1; i <= m; i++) AddEdge(Read(), Read());

    for (int i = 1; i <= n; i++) if (!dfn[i]) Tarjan(i);
    int res = 0;
    for (int i = 1; i <= n; i++) res += ans[i];
    printf("%d\n", res);
    for (int i = 1; i <= n; i++) if (ans[i]) printf("%d ", i);
    putchar('\n');
    return 0;
}

Tarjan 求割边

Luogu T103481 【模板】割边

constexpr int AwA = 5e4 + 10;
constexpr int QwQ = 3e5 + 10;

struct Edge {
    int nxt, v;
} e[QwQ << 1];
int head[AwA], ecnt = 1;

inline void AddEdge(int u, int v) {
    e[++ecnt] = {head[u], v}, head[u] = ecnt;
    e[++ecnt] = {head[v], u}, head[v] = ecnt;
}

int n, m, ans;
int dfn[AwA], low[AwA];

void Tarjan(int u, int ew = 0) {
    dfn[u] = low[u] = ++dfn[0];
    for (int i = head[u]; i; i = e[i].nxt) {
        //防止重边
        if ((ew ^ 1) == i) continue;
        int v = e[i].v;
        if (!dfn[v]) {
            Tarjan(v, i);
            low[u] = min(low[u], low[v]);
            if (low[v] > dfn[u]) ans++;
        } else low[u] = min(low[u], dfn[v]);
    }
}

int main() {
    n = Read(), m = Read();
    for (int i = 1; i <= m; i++) AddEdge(Read(), Read());

    for (int i = 1; i <= n; i++) if (!dfn[i]) Tarjan(i);
    printf("%d\n", ans);
    return 0;
}

点双连通分量

Luogu P8435 【模板】点双连通分量

constexpr int AwA = 5e5 + 10;
constexpr int QwQ = 2e6 + 10;

struct Edge {
    int nxt, v;
} e[QwQ << 1];
int head[AwA], ecnt;

inline void AddEdge(int u, int v) {
    e[++ecnt] = {head[u], v}, head[u] = ecnt;
    e[++ecnt] = {head[v], u}, head[v] = ecnt;
}

int n, m;
int dfn[AwA], low[AwA], stk[AwA], cnt;
vector<int> vdcc[AwA];

void Tarjan(int u, int fa) {
    stk[++stk[0]] = u;
    low[u] = dfn[u] = ++dfn[0];
    int c = 0;
    for (int i = head[u]; i; i = e[i].nxt) {
        int v = e[i].v;
        if (!dfn[v]) {
            c++;
            Tarjan(v, u);
            low[u] = min(low[u], low[v]);
            if (!fa || low[v] >= dfn[u]) {
                vdcc[++cnt].push_back(u);
                for (int k = 0; k != v;) vdcc[cnt].push_back(k = stk[stk[0]--]);
            }
        } else if (v != fa) low[u] = min(low[u], dfn[v]);
    }
    if (!fa && !c) vdcc[++cnt].push_back(u);
}

int main() {
    n = Read(), m = Read();
    while (m--) AddEdge(Read(), Read());
    for (int i = 1; i <= n; i++) if (!dfn[i]) Tarjan(i, 0);

    printf("%d\n", cnt);
    for (int i = 1; i <= cnt; i++) {
        printf("%lld ", vdcc[i].size());
        for (auto k: vdcc[i]) printf("%d ", k);
        putchar('\n');
    }
    return 0;
}

边双连通分量

Luogu P8436 【模板】边双连通分量

constexpr int AwA = 5e5 + 10;
constexpr int QwQ = 2e6 + 10;

struct Edge {
    int nxt, v;
} e[QwQ << 1];
int head[AwA], ecnt = 1;

inline void AddEdge(int u, int v) {
    e[++ecnt] = {head[u], v}, head[u] = ecnt;
    e[++ecnt] = {head[v], u}, head[v] = ecnt;
}

int n, m;
int dfn[AwA], low[AwA], stk[AwA], cnt;
vector<int> edcc[AwA];

void Tarjan(int u, int fae) {
    stk[++stk[0]] = u;
    low[u] = dfn[u] = ++dfn[0];
    for (int i = head[u]; i; i = e[i].nxt) {
        int v = e[i].v;
        if (!dfn[v]) {
            Tarjan(v, i);
            low[u] = min(low[u], low[v]);
            if (low[v] > dfn[u]) {
                edcc[++cnt].push_back(stk[stk[0]]);
                while (stk[stk[0]--] != v) edcc[cnt].push_back(stk[stk[0]]);
            }
        } else if ((i ^ 1) != fae) low[u] = min(low[u], dfn[v]);
    }
    if (!fae) {
        cnt++;
        while (stk[0]) edcc[cnt].push_back(stk[stk[0]--]);
    }
}

int main() {
    n = Read(), m = Read();
    while (m--) AddEdge(Read(), Read());
    for (int i = 1; i <= n; i++) if (!dfn[i]) Tarjan(i, 0);

    printf("%d\n", cnt);
    for (int i = 1; i <= cnt; i++) {
        printf("%lld ", edcc[i].size());
        for (auto k: edcc[i]) printf("%d ", k);
        putchar('\n');
    }
    return 0;
}

2-SAT

Luogu P4782 【模板】2-SAT

static constexpr int AwA = 2e6 + 10;

struct Edge {
    int nxt, v;
} e[AwA];
int head[AwA], ecnt;

inline void AddEdge(int u, int v) { e[++ecnt] = {head[u], v}, head[u] = ecnt; }

int n, m;
int dfn[AwA], low[AwA], co[AwA], stk[AwA];

void Tarjan(int u) {
    stk[++stk[0]] = u;
    dfn[u] = low[u] = ++dfn[0];
    for (int i = head[u]; i; i = e[i].nxt) {
        int v = e[i].v;
        if (!dfn[v]) Tarjan(v), low[u] = min(low[u], low[v]);
        else if (!co[v]) low[u] = min(low[u], dfn[v]);
    }
    if (dfn[u] == low[u]) {
        co[u] = ++co[0];
        while (stk[stk[0]] != u) co[stk[stk[0]--]] = co[0];
        stk[0]--;
    }
}

int main() {
    n = Read(), m = Read();
    int x, y, vx, vy;
    while (m--) {
        x = Read(), vx = Read(), y = Read(), vy = Read();
        AddEdge(x + (vx ^ 1) * n, y + vy * n);
        AddEdge(y + (vy ^ 1) * n, x + vx * n);
    }

    for (int i = 1; i <= 2 * n; i++) if (!dfn[i]) Tarjan(i);
    for (int i = 1; i <= n; i++)
        if (co[i] == co[i + n]) {
            puts("IMPOSSIBLE");
            return 0;
        }
    puts("POSSIBLE");
    //tarjan跑出来的强连通分量顺序为拓扑逆序
    for (int i = 1; i <= n; i++) printf("%d ", co[i] > co[i + n]);
    putchar('\n');
    return 0;
}

欧拉路径(回路)

Luogu P7771 【模板】欧拉路径

constexpr int AwA = 1e5 + 10;
constexpr int QwQ = 2e5 + 10;

int n, m;
vector<int> g[AwA];
int in[AwA], out[AwA];
int stk[QwQ];

void Dfs(int u) {
    while(!g[u].empty()) {
        auto p = g[u].back();
        g[u].pop_back();
        Dfs(p);
    }
    //这里一定要倒序存
    stk[++stk[0]] = u;
}

int main() {
    n = Read(), m = Read();
    int u, v;
    for(int i = 1; i <= m; i++) {
        u = Read(), v = Read();
        g[u].push_back(v);
        in[v]++, out[u]++;
    }
    bool f1 = true, f2 = true;
    for(int i = 1; i <= n; i++) {
        if(in[i] == out[i] - 1) {
            if(!f1)
                return puts("No"), 0;
            else
                f1 = false;
        } else if(out[i] == in[i] - 1) {
            if(!f2)
                return puts("No"), 0;
            else
                f2 = false;
        } else if(in[i] != out[i])
            return puts("No"), 0;
    }
    if(f1 ^ f2) return puts("No"), 0;

    //字典序最大
    for(int i = 1; i <= n; i++) sort(g[i].begin(), g[i].end(), greater<int>());
    if(f1)
        Dfs(1);
    else
        for(int i = 1; i <= n; i++)
            if(out[i] == in[i] + 1) {
                Dfs(i);
                break;
            }
    for(int i = stk[0]; i; i--) printf("%d ", stk[i]);
    putchar('\n');
    return 0;
}

树上问题

倍增LCA

Luogu P3379 【模板】最近公共祖先(LCA)

constexpr int AwA = 5e5 + 10;
constexpr int QwQ = 21;

struct Edge {
    int nxt, v;
} e[AwA << 1];
int head[AwA], ecnt;

inline void AddEdge(int u, int v) {
    e[++ecnt] = {head[u], v};
    head[u] = ecnt;
}

int n, m, rt;
int fa[AwA][QwQ], dep[AwA];

void Dfs(int u, int _fa) {
    fa[u][0] = _fa;
    dep[u] = dep[fa[u][0]] + 1;
    for (int j = 0; j < __lg(dep[u]); fa[u][j + 1] = fa[fa[u][j]][j], j++);
    for (int i = head[u]; i; i = e[i].nxt) if (e[i].v != fa[u][0]) Dfs(e[i].v, u);
}

inline int LCA(int x, int y) {
    if (dep[x] < dep[y]) swap(x, y);
    for (int i = __lg(dep[x] - dep[y]); ~i; i--)
        if (dep[fa[x][i]] >= dep[y]) x = fa[x][i];
    if (x == y) return x;
    for (int i = __lg(dep[x]); ~i; i--)
        if (fa[x][i] != fa[y][i])
            x = fa[x][i], y = fa[y][i];
    return fa[x][0];
}

int main() {
    n = Read(), m = Read(), rt = Read();
    for (int i = 1; i < n; i++) {
        int u = Read(), v = Read();
        AddEdge(u, v);
        AddEdge(v, u);
    }

    Dfs(rt, 0);
    while (m--) printf("%d\n", LCA(Read(), Read()));
    return 0;
}

Dfs序LCA

Luogu P3379 【模板】最近公共祖先(LCA)

(欧拉序求LCA的上位替代)

void Dfs(int u, int fa) {
    //这里一定要存成fa
    st[dfn[u] = ++dfn[0]][0] = {dfn[fa], fa};
    for (int i = head[u]; i; i = e[i].nxt) if (e[i].v != fa) Dfs(e[i].v, u);
}

inline int LCA(int x, int y) {
    //特判
    if (x == y) return x;
    if ((x = dfn[x]) > (y = dfn[y])) swap(x, y);
    //这里要查询[x+1,y]
    int k = __lg(y - x++);
    return min(st[x][k], st[y - (1 << k) + 1][k]).second;
}

inline void InitST() {
    for (int j = 1; j <= __lg(n); j++)
        for (int i = 1; i + (1 << j) - 1 <= n; i++)
            st[i][j] = min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
}

欧拉序LCA

Luogu P3379 【模板】最近公共祖先(LCA)

constexpr int AwA = 5e5 + 10;
constexpr int QwQ = 22;

struct Edge {
    int nxt, v;
} e[AwA << 1];
int head[AwA], ecnt;

inline void AddEdge(int u, int v) {
    e[++ecnt] = {head[u], v};
    head[u] = ecnt;
}

int n, m, rt;
int dep[AwA], dfn[AwA], idfn[AwA << 1];
int f[AwA << 1][QwQ], g[AwA << 1][QwQ];

void Dfs(int u, int fa = 0) {
    dep[u] = dep[fa] + 1;
    idfn[++idfn[0]] = u;
    dfn[u] = idfn[0];
    for (int i = head[u]; i; i = e[i].nxt)
        if (e[i].v != fa) Dfs(e[i].v, u), idfn[++idfn[0]] = u;
}

inline void InitST() {
    for (int i = 1; i <= idfn[0]; i++) f[i][0] = dep[idfn[i]], g[i][0] = idfn[i];
    for (int j = 1; j <= __lg(idfn[0]); j++)
        for (int i = 1; i + (1 << j) - 1 <= idfn[0]; i++) {
            f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
            if (f[i][j - 1] < f[i + (1 << (j - 1))][j - 1]) g[i][j] = g[i][j - 1];
            else g[i][j] = g[i + (1 << (j - 1))][j - 1];
        }
}

inline int QueryMnPos(int l, int r) {
    int k = __lg(r - l + 1);
    if (f[l][k] < f[r - (1 << k) + 1][k]) return g[l][k];
    return g[r - (1 << k) + 1][k];
}

inline int LCA(int x, int y) {
    int l = dfn[x], r = dfn[y];
    if (l > r) swap(l, r);
    return QueryMnPos(l, r);
}

int main() {
    n = Read(), m = Read(), rt = Read();
    for (int i = 1; i < n; i++) {
        int u = Read(), v = Read();
        AddEdge(u, v);
        AddEdge(v, u);
    }

    Dfs(rt);
    InitST();
    while (m--) printf("%d\n", LCA(Read(), Read()));
    return 0;
}

树链剖分

P3384 【模板】重链剖分/树链剖分

typedef long long ll;
constexpr int AwA = 1e5 + 10;

struct Edge {
    int nxt, v;
} e[AwA << 1];
int head[AwA], ecnt;

inline void AddEdge(int u, int v) {
    e[++ecnt] = {head[u], v};
    head[u] = ecnt;
    e[++ecnt] = {head[v], u};
    head[v] = ecnt;
}

int n, m, rt, Mod;
ll sum[AwA << 2], tag[AwA << 2];
int sz[AwA], son[AwA], fa[AwA], top[AwA], dfn[AwA], dep[AwA];
int a[AwA];

void Update(int u, int l, int r, int lx, int rx, int val) {
    if (l == lx && r == rx) {
        tag[u] += val;
        tag[u] %= Mod;
        return;
    }
    sum[u] += 1ll * val * (rx - lx + 1);
    sum[u] %= Mod;
    int mid = (l + r) >> 1;
    if (lx <= mid) Update(u << 1, l, mid, lx, min(mid, rx), val);
    if (mid + 1 <= rx) Update(u << 1 | 1, mid + 1, r, max(lx, mid + 1), rx, val);
}

ll Query(int u, int l, int r, int lx, int rx) {
    ll res = tag[u] * (rx - lx + 1);
    if (lx == l && r == rx) return (res + sum[u]) % Mod;
    int mid = (l + r) >> 1;
    if (lx <= mid) res += Query(u << 1, l, mid, lx, min(mid, rx));
    if (mid + 1 <= rx) res += Query(u << 1 | 1, mid + 1, r, max(mid + 1, lx), rx);
    return res % Mod;
}

void Dfs1(int u, int _fa) {
    sz[u] = 1;
    fa[u] = _fa;
    dep[u] = dep[fa[u]] + 1;
    for (int i = head[u]; i; i = e[i].nxt)
        if (e[i].v != fa[u]) {
            int v = e[i].v;
            Dfs1(v, u);
            if (sz[v] > sz[son[u]]) son[u] = v;
            sz[u] += sz[v];
        }
}

void Dfs2(int u, int _top) {
    top[u] = _top;
    dfn[u] = ++dfn[0];
    if (son[u]) Dfs2(son[u], top[u]);
    for (int i = head[u]; i; i = e[i].nxt)
        if (e[i].v != fa[u] && e[i].v != son[u]) Dfs2(e[i].v, e[i].v);
}

inline void UpdatePath(int val, int x, int y) {
    while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        Update(1, 1, n, dfn[top[x]], dfn[x], val);
        x = fa[top[x]];
    }
    if (dep[x] < dep[y]) swap(x, y);
    Update(1, 1, n, dfn[y], dfn[x], val);
}

inline ll QueryPath(int x, int y) {
    ll res = 0;
    while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        res += Query(1, 1, n, dfn[top[x]], dfn[x]);
        x = fa[top[x]];
    }
    if (dep[x] < dep[y]) swap(x, y);
    res += Query(1, 1, n, dfn[y], dfn[x]);
    return res % Mod;
}

int main() {
    n = Read(), m = Read(), rt = Read(), Mod = Read();
    for (int i = 1; i <= n; i++) a[i] = Read();
    for (int i = 1; i < n; i++) AddEdge(Read(), Read());
    Dfs1(rt, 0);
    Dfs2(rt, rt);
    for (int i = 1; i <= n; i++) Update(1, 1, n, dfn[i], dfn[i], a[i]);

    while (m--) {
        int op = Read();
        if (op == 1) UpdatePath(Read(), Read(), Read());
        else if (op == 2) printf("%lld\n", QueryPath(Read(), Read()));
        else if (op == 3) {
            int u = Read(), v = Read();
            Update(1, 1, n, dfn[u], dfn[u] + sz[u] - 1, v);
        } else {
            int u = Read();
            printf("%lld\n", Query(1, 1, n, dfn[u], dfn[u] + sz[u] - 1));
        }
    }
    return 0;
}

动态规划

01背包

P1048 【NOIP2005 普及组】 采药

constexpr int AwA = 1e2 + 10;
constexpr int QwQ = 1e3 + 10;

int n, m;
int v[AwA], w[AwA];
int f[QwQ];

int main() {
    m = Read(), n = Read();
    for (int i = 1; i <= n; i++) v[i] = Read(), w[i] = Read();
    //这里枚举j要倒序
    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]);
    printf("%d\n", f[m]);
    return 0;
}

完全背包

没找到模板题抱歉喵~

constexpr int AwA = 1e2 + 10;
constexpr int QwQ = 1e3 + 10;

int n, m;
int v[AwA], w[AwA];
int f[QwQ];

int main() {
    m = Read(), n = Read();
    for (int i = 1; i <= n; i++) v[i] = Read(), w[i] = Read();
    //这里枚举j要正序,这是和01背包唯一的一个区别
    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]);
    printf("%d\n", f[m]);
    return 0;
}

字符串

哈希

Luogu P3370 【模板】字符串哈希

constexpr int AwA = 1e4 + 10;

struct Hash {
    static constexpr int Mod1 = 1e9 + 7;
    static constexpr int Mod2 = 1e9 + 9;
    static constexpr int P = 131;

    //双哈希
    int h1, h2;

    //空构造为默认构造
    Hash() = default;

    //将给定的字符串视为一个P进制数计算哈希值
    Hash(const char *s) {
        //默认字符串数组下标从1开始
        h1 = h2 = 0;
        int len = int(strlen(s + 1));
        for (int i = 1; i <= len; i++) {
            h1 = int((1ll * h1 * P + s[i]) % Mod1);
            h2 = int((1ll * h2 * P + s[i]) % Mod2);
        }
    }

    //sort要用,使相等的哈希相邻
    inline bool operator<(const Hash &hh) const {
        return h1 < hh.h1 || (h1 == hh.h1 && h2 < hh.h2);
    }

    //unique要用,当且仅当两个哈希值都相等认为两个字符串相等
    inline bool operator==(const Hash &hh) const {
        return h1 == hh.h1 && h2 == hh.h2;
    }
} h[AwA];

int n;
char s[AwA];

int main() {
    n = Read();
    for (int i = 1; i <= n; i++) {
        scanf("%s", s + 1);
        h[i] = Hash(s);
    }

    sort(h + 1, h + n + 1);
    int ans = int(unique(h + 1, h + n + 1) - h - 1);
    printf("%d\n", ans);
    return 0;
}

哈希表

Luogu P3370 【模板】字符串哈希

typedef unsigned long long ull;
static constexpr int P = 131;
static constexpr int AwA = 1e4 + 10;

class HashTable {
private:
    //笔者常用的哈希表质数
    static constexpr int Mod = 1e6 + 3;

    struct Node {
        int nxt;
        ull val;
    } p[AwA];
    int head[Mod], pcnt;
public:
    inline void Clear() {
        memset(head, 0, sizeof head);
        pcnt = 0;
    }

    //若待插入的数已在哈希表中返回false,否则插入
    inline bool Insert(ull val) {
        if (Find(val)) return false;
        int hs = int(val % Mod);
        p[++pcnt] = {head[hs], val};
        head[hs] = pcnt;
        return true;
    }

    //查找当前的数是否已在哈希表中
    inline bool Find(ull val) {
        int hs = int(val % Mod);
        for (int i = head[hs]; i; i = p[i].nxt)
            if (p[i].val == val) return true;
        //未找到
        return false;
    }
} mp;

inline ull Hash(const char *s) {
    ull h = 0;
    int len = int(strlen(s + 1));
    //自然溢出,即模2的64次方
    for (int i = 1; i <= len; i++) h = h * P + s[i];
    return h;
}

int n, ans;
char s[AwA];

int main() {
    n = Read();
    for (int i = 1; i <= n; i++) {
        scanf("%s", s + 1);
        if (mp.Insert(Hash(s))) ans++;
    }
    printf("%d\n", ans);
    return 0;
}

KMP

Luogu P3375 【模板】KMP

constexpr int AwA = 1e6 + 10;

int n, m;
char s1[AwA], s2[AwA];
int p[AwA];

inline void Pre() {
    p[1] = 0;
    for (int i = 2, j = 0; i <= m; i++) {
        while (j && s2[j + 1] != s2[i]) j = p[j];
        if (s2[j + 1] == s2[i]) j++;
        p[i] = j;
    }
}

inline void Kmp() {
    for (int i = 1, j = 0; i <= n; i++) {
        while (j && s2[j + 1] != s1[i]) j = p[j];
        if (s2[j + 1] == s1[i]) j++;
        if (j == m) printf("%d\n", i - m + 1), j = p[j];
    }
}

int main() {
    scanf("%s%s", s1 + 1, s2 + 1);
    n = int(strlen(s1 + 1)), m = int(strlen(s2 + 1));

    Pre();
    Kmp();

    for (int i = 1; i <= m; i++) printf("%d ", p[i]);
    putchar('\n');
    return 0;
}

Trie树

Luogu P8306 【模板】字典树

constexpr int AwA = 3e6 + 10;

struct Node {
    int ch[62];
    int cnt;
} tr[AwA];

int tot;
char s[AwA];

inline int CharToInt(char c) {
    if (c <= '9') return c - '0' + 52;
    if (c >= 'a') return c - 'a' + 26;
    return c - 'A';
}

inline int NewNode() {
    tot++;
    memset(tr[tot].ch, 0, sizeof(int) * 62);
    tr[tot].cnt = 0;
    return tot;
}

inline void Insert() {
    int u = 1, len = int(strlen(s + 1));
    for (int i = 1; i <= len; i++) {
        int c = CharToInt(s[i]);
        if (!tr[u].ch[c]) tr[u].ch[c] = NewNode();
        u = tr[u].ch[c];
        tr[u].cnt++;
    }
}

int main() {
    int T = Read();
    while (T--) {
        tot = 0;
        NewNode();

        int n = Read(), m = Read();
        for (int i = 1; i <= n; i++) {
            scanf("%s", s + 1);
            Insert();
        }

        while (m--) {
            scanf("%s", s + 1);
            int u = 1, len = int(strlen(s + 1));
            for (int i = 1; i <= len && u; i++) {
                int c = CharToInt(s[i]);
                u = tr[u].ch[c];
            }
            printf("%d\n", tr[u].cnt);
        }
    }

    return 0;
}

Manacher

Luogu P3805 【模板】manacher

static constexpr int AwA = 1.1e7 + 10;

int m, n;
//两个数组注意开2倍
char s1[AwA], s[AwA << 1];
int d[AwA << 1];
int ans;

int main() {
    scanf("%s", s1 + 1);
    m = int(strlen(s1 + 1));

    //处理字符串
    n = 2 * m + 1;
    s[0] = '?', s[1] = '#';
    for (int i = 1; i <= m; i++) {
        s[i * 2] = s1[i];
        s[i * 2 + 1] = '#';
    }
    s[n + 1] = '\0';

    d[1] = 1;
    for (int i = 2, l = 1, r = 1; i <= n; i++) {
        if (i <= r) d[i] = min(r - i + 1, d[r - i + l]);
        while (s[i + d[i]] == s[i - d[i]]) d[i]++;
        if (i + d[i] - 1 >= r) r = i + d[i] - 1, l = i - d[i] + 1;
    }

    //回文子串长度=处理后的回文半径-1
    for (int i = 1; i <= n; i++) ans = max(ans, d[i] - 1);
    printf("%d\n", ans);
    return 0;
}

数学

线性基

Luogu P3812 【模板】线性基

class Basis {
public:
    int cnt;
    ll d[64];
    inline void operator+=(ll x) {
        for(int i = 63; ~i; i--)
            if(x >> i & 1) {
                if(!d[i]) {
                    d[i] = x;
                    cnt++;
                    break;
                } else
                    x ^= d[i];
            }
    }
} bs;

int main() {
    for(int i = Read(); i; i--) bs += Read();
    ll ans = 0;
    for(int i = 63; ~i; i--)
        if((ans ^ bs.d[i]) > ans) ans ^= bs.d[i];
    printf("%lld\n", ans);
    return 0;
}

矩阵快速幂

Luogu P3390 【模板】矩阵快速幂

常用的矩阵操作基本都放在这里了喵

typedef long long ll;
constexpr int AwA = 1e2 + 10;
constexpr int Mod = 1e9 + 7;

class Matrix {
private:
    //n是行数,m是列数
    int n, m;
    ll a[AwA][AwA];

public:
    Matrix() = default;

    Matrix(int n, int m) : n(n), m(m) { memset(a, 0, sizeof a); }

    //方便调用
    inline ll *operator[](int x) { return a[x]; }

    //-同理不写了
    inline Matrix operator+(Matrix &m1) const {
        Matrix res(n, m);
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
                res[i][j] = (a[i][j] + m1[i][j]) % Mod;
        return res;
    }

    inline Matrix operator*(Matrix &m1) const {
        int q = m1.m;
        ll r;
        Matrix res(n, q);
        //换枚举方式减小常数
        for (int i = 1; i <= n; i++)
            for (int k = 1; k <= m; k++) {
                r = a[i][k];
                for (int j = 1; j <= q; j++)
                    res[i][j] = (res[i][j] + r * m1[k][j]) % Mod;
            }
        return res;
    }

    inline Matrix operator^(ll k) const {
        Matrix res(n, n);
        for (int i = 1; i <= n; i++) res[i][i] = 1;
        Matrix b(n, n);
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                b[i][j] = a[i][j];

        while (k) {
            if (k & 1) res = res * b;
            b = b * b;
            k >>= 1;
        }
        return res;
    }
};

int main() {
    int n = Read();
    ll k = Read<ll>();

    Matrix a(n, n);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            a[i][j] = Read();

    Matrix res = a ^ k;
    for (int i = 1; i <= n; i++, putchar('\n'))
        for (int j = 1; j <= n; j++)
            printf("%lld ", res[i][j]);

    return 0;
}
posted @ 2024-04-07 08:12  Sugar_Cube  阅读(283)  评论(1编辑  收藏  举报