快轉到主要內容

競程小抄

目錄

預設模板
#

#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define fi first
#define se second
#define INF LONG_LONG_MAX/1000
#define WA() cin.tie(0)->sync_with_stdio(0)
#define all(x) (x).begin(), (x).end()
#define int long long
#define PII pair<int, int>

2D Prefix Sum
#

int row, col;
while (cin >> row >> col) {
    vector<vector<int>> v(row+1, vector<int>(col+1));
    for (int i = 1, tmp; i <= row; i++) for (int j = 1; j <= col; j++) {
        cin >> v[i][j];
        v[i][j] += v[i-1][j] - v[i-1][j-1] + v[i][j-1];
    }
    int sx, sy, ex, ey;
    cin >> sx >> sy >> ex >> ey;
    cout << v[ex][ey]-v[ex][sy-1]-v[sx-1][ey]+v[sx-1][sy-1] << '\n';
}

Union-Find
#

vector<int> par(N), sz(N, 1); // N 為節點總數
int Find(int x) { return par[x] == x ? x : par[x] = Find(par[x]); }
void Union(int x, int y) {
    x = Find(x); y = Find(y);
    if (x == y) return;
    if (sz[x] < sz[y]) swap(x, y);
    par[y] = x; sz[x] += sz[y]; sz[y] = 0;
}
int getSize(int x) { return sz[Find(x)]; }
// iota(all(par), 0);

undo Disjoint Set
#

struct DisjointSet {
    int n, fa[MXN], sz[MXN]; // save() is like recursive
    vector<pair<int*,int>> h; // undo() is like return
    vector<int> sp;
    void init(int tn) {
        n=tn;
        for (int i=0; i<n; i++) sz[fa[i]=i]=1;
        sp.clear(); h.clear();
    }
    void assign(int *k, int v) {
        h.PB({k, *k});
        *k=v;
    }
    void save() { sp.PB(SZ(h)); }
    void undo() {
        assert(!sp.empty());
        int last=sp.back(); sp.pop_back();
        while (SZ(h)!=last) {
            auto x=h.back(); h.pop_back();
            *x.F=x.S;
        }
    }
    int f(int x) {
        while (fa[x]!=x) x=fa[x];
        return x;
    }
    void uni(int x, int y) {
        x=f(x); y=f(y);
        if (x==y) return;
        if (sz[x]<sz[y]) swap(x, y);
        assign(&sz[x], sz[x]+sz[y]);
        assign(&fa[y], x);
    }
} djs;
int main() {
    djs.init(5);
    djs.save();
    djs.uni(0,1);
    djs.uni(2,3);
    cout << (djs.f(0)==djs.f(1)) << "\n"; // 1
    djs.undo(); 
    djs.uni(1,2);
    cout << (djs.f(1)==djs.f(2)) << "\n"; // 1
}

BIT (Fenwick Tree)
#

vector<int> v, bit;
int n;
void add(int x, int val) { for (; x <= n; x += x&-x) bit[x] += val; }
int query(int x) { 
    int sum = 0;
    for (; x > 0; x -= x&-x) sum += bit[x];
    return sum;
}
int main() {
    int a, b; cin >> n >> a >> b;
    v.resize(n);
    for (auto &i : v) cin >> i;
    bit.assign(n+1, 0);
    for (int i = 0; i< n; i++) add(i+1, v[i]);
    cout << query(b) - query(a-1);
}

線段樹
#

int n; // n 為節點數
struct Node {
    int data, tag;
    Node() : data(0), tag(0) {}
};
vector<Node> seg;
int get_val(int l, int r, int id) {
    return seg[id].data + seg[id].tag * (r-l+1);
}
void pull(int l, int r, int id) {
    int m = (l+r)>>1;
    seg[id].data = get_val(l, m, id<<1) + get_val(m+1, r, id<<1|1);
}
void push(int l, int r, int id) {
    if (seg[id].tag) {
        seg[id].data += seg[id].tag * (r-l+1);
        seg[id<<1].tag += seg[id].tag;
        seg[id<<1|1].tag += seg[id].tag;
        seg[id].tag = 0;
    }
}
void build(vector<int> &v, int l = 0, int r = n-1, int id = 1) {
    if (l == r) return void(seg[id].data = v[l]);
    int m = (l+r)>>1;
    build(v, l, m, id<<1);
    build(v, m+1, r, id<<1|1);
    seg[id].data = seg[id<<1].data + seg[id<<1|1].data;
}
void update(int ql, int qr, int v, int l = 0, int r = n-1, int id = 1) {
    if (l > qr || r < ql) return;
    if (l >= ql && r <= qr) return void(seg[id].tag += v);
    push(l, r, id); int m = (l+r)>>1;
    update(ql, qr, v, l, m, id<<1);
    update(ql, qr, v, m+1, r, id<<1|1);
    pull(l, r, id);
}
int query(int ql, int qr, int l = 0, int r = n-1, int id = 1) {
    if (l > qr || r < ql) return 0;
    if (l >= ql && r <= qr) return get_val(l, r, id);
    push(l, r, id); int m = (l+r)>>1;
    return query(ql, qr, l, m, id<<1) + query(ql, qr, m+1, r, id<<1|1); 
}
signed main() {
    cin >> n; vector<int> v(n);
    seg.assign(n<<2|1, Node());
    for (auto &i : v) cin >> i;
    build(v);
    cout << query(0, 3) << '\n';
    update(1, 3, 5);
    cout << query(0, 3) << '\n';
}

樹序列化
#

int timer, pos[MAXN], sz[MAXN];
vector<int> g[MAXN]; // 樹的鄰接表
void dfs(int now, int pa) {
    pos[now] = ++timer;   // 紀錄節點在序列中的位置
    sz[now] = 1;          // 子樹大小初始化為1(包含自己)
    for (int v : g[now]) {
        if (v == pa) continue;
        dfs(v, now);
        sz[now] += sz[v]; // 累加子節點的大小
    }
}

ODT tree
#

struct Node {
    int l, r;
    mutable int v;
    Node(const int &il, const int &ir, const int &iv): l(il), r(ir), v(iv) {}
    bool operator<(const Node &o) const { return l < o.l; }
};
set<Node> odt; const int p = 1e9+7;
inline Node *_copy(set<Node>::iterator x) { return new Node(x->l, x->r, x->v); }
auto _split(int x) {
    auto it = odt.lower_bound(Node(x, 0, 0));
    if (it != odt.end() && it->l == x) return it;
    auto k = _copy(--it);
    odt.erase(it);
    odt.insert(Node(k->l, x-1, k->v));
    return odt.insert(Node(x, k->r, k->v)).fi;
}
int _query(int l, int r) {
    auto itr = _split(r+1), itl = _split(l);
    int sum = 0;
    for (; itl != itr; itl++) sum = (sum + itl->v%p * (itl->r - itl->l + 1)%p) % p;
    return sum;
}
void _assign(int l, int r, int v) {
    auto itr = _split(r+1), itl = _split(l);
    odt.erase(itl, itr);
    odt.insert(Node(l, r, v));
}
void _add(int l, int r, int v) {
    auto itr = _split(r+1), itl = _split(l);
    vector<Node> vec;
    for (auto it = itl; it != itr; it++) vec.emplace_back(*it), vec.back().v = (vec.back().v+v) % p;
    odt.erase(itl, itr);
    for (auto &i : vec) odt.insert(i);
}
void _dup(int l1, int r1, int l2, int r2) {
    auto itr1 = _split(r1+1), itl1 = _split(l1);
    vector<Node> vec;
    for (; itl1 != itr1; itl1++) vec.emplace_back(*itl1);
    auto itr2 = _split(r2+1), itl2 = _split(l2);
    odt.erase(itl2, itr2);
    for (auto &i : vec) odt.insert(Node(i.l-l1+l2, i.r-l1+l2, i.v));
}
void _swap(int l1, int r1, int l2, int r2) {
    if (l1 > l2) { swap(l1, l2); swap(r1, r2); }
    auto itr1 = _split(r1+1), itl1 = _split(l1);
    vector<Node> va, vb;
    for (auto it = itl1; it != itr1; it++) va.emplace_back(*it);
    auto itr2 = _split(r2+1), itl2 = _split(l2);
    for (auto it = itl2; it != itr2; it++) vb.emplace_back(*it);
    itr1 = _split(r1+1), itl1 = _split(l1); // 記得重新 split (l1, r1),因為經過了 split (l2, r2) set 內已經變了
    odt.erase(itl1, itr1);
    itr2 = _split(r2+1), itl2 = _split(l2); // 同理
    odt.erase(itl2, itr2);
    for (auto &i : va) odt.insert(Node(i.l-l1+l2, i.r-l1+l2, i.v));
    for (auto &i : vb) odt.insert(Node(i.l-l2+l1, i.r-l2+l1, i.v));
}

void _rev(int l, int r) {
    if (l > r) swap(l, r); // !
    auto itr = _split(r+1), itl = _split(l);
    vector<Node> vec;
    for (auto it = itl; it != itr; it++) vec.emplace_back(*it);
    odt.erase(itl, itr);
    for (auto &i : vec) odt.insert(Node(r-i.r+l, r-i.l+l, i.v));
}
signed main() { WA();
    int n, m; cin >> n >> m;
    for (int i = 0; i < n; i++) {
        int x; cin >> x;
        odt.insert(Node(i, i, x));
    }
    while (m--) {
        int cmd, l1, l2, r1, r2, val;
        cin >> cmd >> l1 >> r1; l1--, r1--;
        if (cmd == 1) cout << _query(l1, r1) << '\n';
        else if (cmd == 2) {
            cin >> val;
            _assign(l1, r1, val);
        } else if (cmd == 3) {
            cin >> val;
            _add(l1, r1, val);
        } else if (cmd == 4) {
            cin >> l2 >> r2; l2--, r2--;
            _dup(l1, r1, l2, r2);
        } else if (cmd == 5) {
            cin >> l2 >> r2; l2--, r2--;
            _swap(l1, r1, l2, r2);
        } else _rev(l1, r1);
    }
    for (auto &i : odt) for (int j = i.l; j <= i.r; j++) cout << i.v%p << ' ';
}

String
#

KMP
#

string text, pattern;
cin >> text >> pattern;
string m = pattern + "#" + text;
int n = m.size(); vector<int> p(n);
for (int i = 1; i < n; i++) {
    int j = p[i-1];
    while (j > 0 && m[i] != m[j]) j = p[j-1];
    p[i] = j + (m[i] == m[j]);
}
int i;
for (i = pattern.size()+1; i < n; i++) if (p[i] == pattern.size()) break;
if (i == n) cout << "n\n";
else cout << "y\n";

Manacher
#

const int N = 2e7+10;
string s, t;      // 原串, 改造串
int p[N], n;      // p[i] 表示以 t[i] 為中心的回文子串半徑, 改造串長度
void manacher() {
    int l = 0, r = 0;
    for (int i = 1; i < n - 1; i++) {
        if (i <= r) p[i] = min(p[l + r - i], r - i + 1);
        else p[i] = 1;
        while (t[i + p[i]] == t[i - p[i]]) p[i]++;
        if (i + p[i] - 1 > r) {
            l = i - p[i] + 1;
            r = i + p[i] - 1;
        }
    }
}

int main() {
    cin >> s;
    t = "@#"; // 預處理,插入特殊字符 #,首尾加哨兵
    for (char c : s) {
        t += c; t += '#';
    }
    t += '$'; n = t.size();
    manacher();
    int maxLen = 0; // 計算最大回文子串長度(原串長度)
    for (int i = 1; i < n - 1; i++) maxLen = max(maxLen, p[i] - 1);
    cout << maxLen << "\n";
}

Trie
#

struct Node {
    unordered_map<char, Node*> child;
    bool isWord = false;
};
void insert(Node *p, string s) {
    for (auto c : s) {
        if (!p -> child[c]) p -> child[c] = new Node;
        p = p -> child[c];
    } p -> isWord = true;
}
bool search(Node *p, string s) {
    for (auto c : s) {
        if (p -> child[c]) p = p -> child[c];
        else return false;
    } return p -> isWord;
}
Node *remove(Node *root, string s, int depth = 0) {
    if (!root) return nullptr;
    if (depth == s.size()) {
        if (root -> isWord) root -> isWord = false;
        if (root -> child.empty()) {
            delete root;
            root = nullptr;
        } return root;
    }
    char c = s[depth];
    if (root -> child[c]) {
        root -> child[c] = remove(root -> child[c], s, depth + 1);
        if (root -> child.empty() && !root -> isWord) {
            delete root;
            root = nullptr;
        } return root;
    } return root;
}
int main() { WA();
    int t; for (cin >> t; t; t--) {
        int n;
        Node *root = new Node; cin >> n;
        vector<string> v(n);
        for (auto &i : v) cin >> i;
        for (auto i : v) insert(root, i);
    }
}

AC 自動機
#

const int MAXS = 500, MAXC = 26; // 狀態數最大值(所有模式串長度和), 字母表大小 (a~z)
int out[MAXS], f[MAXS], g[MAXS][MAXC]; // 輸出函數, 失配函數, Trie的轉移函數
// 建立匹配自動機,arr是模式串數組,k是模式數
int buildMatchingMachine(string arr[], int k) {
    memset(out, 0, sizeof out); memset(g, -1, sizeof g);
    int states = 1;
    for (int i = 0; i < k; ++i) { // 建立Trie結構
        const string &word = arr[i];
        int currentState = 0;
        for (int j = 0; j < (int)word.size(); ++j) {
            int ch = word[j] - 'a';
            if (g[currentState][ch] == -1) g[currentState][ch] = states++;
            currentState = g[currentState][ch];
        } out[currentState] |= (1 << i); // 標記該狀態可匹配字串i
    }
    for (int ch = 0; ch < MAXC; ++ch) // 對Trie根節點沒有轉移的字元,設定回到根節點
        if (g[0][ch] == -1)
            g[0][ch] = 0;
    memset(f, -1, sizeof f); queue<int> q; // f[0] = 0
    for (int ch = 0; ch < MAXC; ++ch) { // 初始化深度為1的節點,失配指標為0
        if (g[0][ch] != 0) {
            f[g[0][ch]] = 0;
            q.push(g[0][ch]);
        }
    }
    while (!q.empty()) { // BFS求失配指標
        int state = q.front(); q.pop();
        for (int ch = 0; ch < MAXC; ++ch) {
            if (g[state][ch] != -1) {
                int failure = f[state];
                while (g[failure][ch] == -1)
                    failure = f[failure];
                failure = g[failure][ch];
                f[g[state][ch]] = failure;
                out[g[state][ch]] |= out[failure];
                q.push(g[state][ch]);
            }
        }
    } return states;
} // 找下一狀態 using goto和失配函數
int findNextState(int currentState, char nextInput) {
    int answer = currentState, ch = nextInput - 'a';
    while (g[answer][ch] == -1) answer = f[answer];
    return g[answer][ch];
} // 搜尋text中出現的所有模式串
void searchWords(string arr[], int k, string text) {
    buildMatchingMachine(arr, k);
    int currentState = 0;
    for (int i = 0; i < (int)text.size(); ++i) {
        currentState = findNextState(currentState, text[i]);
        if (out[currentState] == 0) continue;
        for (int j = 0; j < k; ++j) 
            if (out[currentState] & (1 << j))
                cout << "Word \"" << arr[j] << "\" appears from "
                << i - (int)arr[j].size() + 1 << " to " << i << endl;
    }
}
int main() {
    string arr[] = {"he", "she", "hers", "his"}, text = "ahishers";
    int k = sizeof(arr) / sizeof(arr[0]);
    searchWords(arr, k, text);
}

Graph
#

鏈式前向星
#

const int MAXN = 10005;  // 頂點數量上限
const int MAXM = 200005; // 邊數量上限 (無向圖需2倍)

struct Edge {
    int to;    // 邊的終點
    int next;  // 指向上一條邊的索引(同一出發點的前一條邊)
    int w;     // 邊權值(若無權可省略)
} edges[MAXM];

int head[MAXN];   // head[i]儲存從節點i出發的第一條邊的編號
int edgeCount;    // 已加邊數量計數器

void init() { // 初始化head,表示節點尚無出邊
    memset(head, -1, sizeof(head));
    edgeCount = 0;
}

void addEdge(int u, int v, int w = 0) { // 加邊 (u -> v, 權重w)
    edges[edgeCount].to = v;
    edges[edgeCount].w = w;
    edges[edgeCount].next = head[u];
    head[u] = edgeCount++;
}

void traverse(int u) { // 遍歷節點u的所有出邊
    for (int i = head[u]; i != -1; i = edges[i].next) {
        int v = edges[i].to;
        int w = edges[i].w; // 對出邊執行操作,例如輸出v和w
        cout << "邊: " << u << " -> " << v << ", 權重: " << w << endl;
    }
}

Dijkstra
#

int main() {
    int n, m; cin >> n >> m; // 節點數、邊數
    vector<int> v(n), dis(n, INF);
    vector<vector<PII>> g(n);
    for (auto &i : v) cin >> i;
    while (m--) {
        int a, b, w;
        cin >> a >> b >> w;
        a--; b--;
        g[a].pb({b, w}); g[b].pb({a, w});
    }
    priority_queue<PII, vector<PII>, greater<PII>> pq;
    dis[0] = v[0]; pq.push({v[0], 0}); // 0 為起點
    while (!pq.empty()) {
        auto [d, node] = pq.top(); pq.pop();
        if (d > dis[node]) continue;
        for (auto &[to_idx, to_dis] : g[node])
            if (dis[node] + to_dis < dis[to_idx]) {
                dis[to_idx] = dis[node] + to_dis;
                pq.push({dis[to_idx], to_idx});
            }
    }
    for (int i = 1; i < n; i++) {
        if (dis[i] == INF) cout << "-1 ";
        else cout << dis[i] << ' ';
    }
}

Kruskal
#

vector<int> anc;
int Find(int x) { return (anc[x] == x ? x : anc[x] = Find(anc[x])); }
bool Union(int a, int b) {
    int fa = Find(a), fb = Find(b);
    if (fa > fb) swap(fa, fb);
    if (fa == fb) return false;
    return anc[fb] = fa, true;
}
anc.resize(n);
iota(all(anc), 0);
g.pb({w, a, b});
sort(all(g));
for (auto &[w, a, b] : g) if (Union(a, b)) ans += w;

Topological-Sorting
#

int main() {
    int n, m, a, b;
    while (cin >> n >> m) {
        vector<vector<int>> v(n);
        vector<int> w(n), ans;
        while (m--) {
            cin >> a >> b;
            v[--a].pb(--b); w[b]++;
        }
        queue<int> q;
        for (int i = 0; i < n; i++) if (!w[i]) q.push(i);
        while (q.size()) {
            cout << q.front()+1 << ' ';
            for (auto i : v[q.front()]) if (!(--w[i])) q.push(i);
            q.pop();
        }
        cout << '\n';
    }
}

LCA
#

vector<vector<int>> v, par;
vector<int> dep;
void dfs(int x, int u) {
    par[0][x] = u;
    for (auto &i : v[x]) {
        if (i == u) continue;
        dep[i] = dep[x]+1; dfs(i, x);
    }
}
int lca(int a, int b) {
    if (dep[a] < dep[b]) swap(a, b);
    for (int i = 19; i >= 0; i--) if (dep[par[i][a]] >= dep[b]) a = par[i][a];
    if (a == b) return a;
    for (int i = 19; i >= 0; i--) if (par[i][a] != par[i][b])
        a = par[i][a], b = par[i][b];
    return par[0][a];
}
signed main() { WA();
    int n, q; cin >> n >> q;
    v.resize(n+1);
    for (int i = 2; i <= n; i++) {
        int k; cin >> k;
        v[i].pb(k); v[k].pb(i);
    }
    par.resize(20, vector<int>(n+1));
    dep.resize(n+1);
    dfs(1, 1); // 從根結點開始建深度表
    for (int i = 1; i < 20; i++) for (int j = 1; j <= n; j++)
        par[i][j] = par[i-1][par[i-1][j]];
    while (q--) {
        int a, b; cin >> a >> b;
        cout << lca(a, b) << '\n';
    }
}

Maximum Flow (Edmonds-Karp)
#

int n, s, e, t, a, b, w;
vector<vector<int>> g;
vector<int> vis, par;
bool BFS() {
    queue<int> q;
    vis.assign(n+1, 0); par.assign(n+1, 0);
    q.push(s); vis[s] = 1;
    while (q.size()) {
        int now = q.front(); q.pop();
        if (now == e) return true;
        for (int i = 1; i <= n; i++) if (g[now][i] && !vis[i]) {
            vis[i] = 1; par[i] = now; q.push(i);
        }
    } return false;
}
signed main() { WA();
    int c = 0;
    while (cin >> n, n) {
        g.assign(n+1, vector<int>(n+1, 0));
        cin >> s >> e >> t;
        while (t--) {
            cin >> a >> b >> w;
            g[a][b] += w; g[b][a] += w;
        }
        int ans = 0;
        while (BFS()) {
            int now = e, mnF = INF;
            while (now != s) {
                mnF = min(mnF, g[par[now]][now]);
                now = par[now];
            }
            now = e;
            while (now != s) {
                g[par[now]][now] -= mnF;
                g[now][par[now]] += mnF;
                now = par[now];
            }
            ans += mnF;
        }
        cout << ans;
    }
}

Dinic + MCMF 回傳最大流數值
#

struct FlowEdge {
    int v, u;
    long long cap, flow = 0;
    FlowEdge(int v, int u, long long cap) : v(v), u(u), cap(cap) {}
};

struct Dinic {
    const long long flow_inf = 1e18;
    vector<FlowEdge> edges;
    vector<vector<int>> adj;
    int n, m = 0, s, t;
    vector<int> level, ptr;
    queue<int> q;
    Dinic(int n, int s, int t) : n(n), s(s), t(t) {
        adj.resize(n); level.resize(n); ptr.resize(n);
    }
    void add_edge(int v, int u, long long cap) {
        edges.emplace_back(v, u, cap);
        edges.emplace_back(u, v, 0);
        adj[v].push_back(m);
        adj[u].push_back(m + 1); m += 2;
    }
    bool bfs() {
        while (!q.empty()) q.pop();
        fill(level.begin(), level.end(), -1);
        level[s] = 0; q.push(s);
        while (!q.empty()) {
            int v = q.front();
            q.pop();
            for (int id : adj[v]) {
                if (edges[id].cap == edges[id].flow) continue;
                if (level[edges[id].u] != -1) continue;
                level[edges[id].u] = level[v] + 1;
                q.push(edges[id].u);
            }
        }
        return level[t] != -1;
    }

    long long dfs(int v, long long pushed) {
        if (pushed == 0) return 0;
        if (v == t) return pushed;
        for (int &cid = ptr[v]; cid < (int)adj[v].size(); cid++) {
            int id = adj[v][cid];
            int u = edges[id].u;
            if (level[v] + 1 != level[u]) continue;
            long long tr = dfs(u, min(pushed, edges[id].cap - edges[id].flow));
            if (tr == 0) continue;
            edges[id].flow += tr;
            edges[id ^ 1].flow -= tr;
            return tr;
        } return 0;
    }
    long long flow() {
        long long f = 0;
        while (true) {
            if (!bfs()) break;
            fill(ptr.begin(), ptr.end(), 0);
            while (long long pushed = dfs(s, flow_inf)) {
                f += pushed;
            }
        } return f;
    }
};

SPFA
#

const int N = 100010;
int e[N], ne[N], h[N], w[N], idx; // 鄰接表: e儲存邊的終點, ne為下一條邊, h為頭指標, w為權值
int n, m, dist[N], cnt[N];        // dist:起點到各點距離, cnt:計數攜帶負環判斷
bool st[N];                      // st標記節點是否在隊列中
void add(int a, int b, int c) {
    e[idx] = b; w[idx] = c; ne[idx] = h[a]; h[a] = idx++;
}
bool spfa(int s) {
    memset(dist, 0x3f, sizeof dist);
    memset(cnt, 0, sizeof cnt);
    memset(st, 0, sizeof st);
    dist[s] = 0;
    queue<int> q;
    q.push(s);
    st[s] = true;
    while (!q.empty()) {
        int t = q.front();
        q.pop();
        st[t] = false;
        for (int i = h[t]; i != -1; i = ne[i]) {
            int j = e[i];
            if (dist[j] > dist[t] + w[i]) {
                dist[j] = dist[t] + w[i];
                cnt[j] = cnt[t] + 1;
                if (cnt[j] >= n) return true; // 判斷負環,遍歷超過n次表示有負環
                if (!st[j]) {
                    q.push(j); st[j] = true;
                }
            }
        }
    }
    return false; // 無負環
}
int main() {
    cin >> n >> m;
    memset(h, -1, sizeof h);
    for (int i = 0; i < m; i++) {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);
    }
    int start = 1; // 起點可按需設置
    if (spfa(start)) cout << "存在負權環\n";
    else {
        for (int i = 1; i <= n; i++) {
            if (dist[i] == 0x3f3f3f3f) cout << "不可達 ";
            else cout << dist[i] << " ";
        } cout << "\n";
    }
}

Tarjan’s Algorithm for AP/Bridge
#

struct AP_Bridge {
    int n;                          
    vector<vector<int>> G;          // 圖的鄰接表
    vector<int> low, depth, AP;         
    vector<bool> visited;          
    vector<pair<int,int>> Bridge;   // 橋邊列表
    AP_Bridge(int n): n(n) {
        G.assign(n, vector<int>());
        low.assign(n, 0);
        depth.assign(n, 0);
        visited.assign(n, false);
        AP.clear(); Bridge.clear();
    }
    void add_edge(int u, int v) {
        G[u].push_back(v);
        G[v].push_back(u);
    }
    void dfs(int u, int parent, int d) {
        visited[u] = true;
        depth[u] = low[u] = d;
        int child = 0; bool isAP = false;
        for (int v : G[u]) {
            if (v == parent) continue;
            if (!visited[v]) {
                dfs(v, u, d + 1);
                low[u] = min(low[u], low[v]);
                if (low[v] >= depth[u]) isAP = true;
                if (low[v] > depth[u]) Bridge.emplace_back(u, v);
                child++;
            } else low[u] = min(low[u], depth[v]);
        }
        if ((parent == -1 && child > 1) || (parent != -1 && isAP)) AP.push_back(u);
    }
    void solve() { for (int i = 0; i < n; i++) if (!visited[i]) dfs(i, -1, 1); }
};

Kosaraju
#

void dfs1(int u, const vector<vector<int>>& g, vector<bool>& visited, stack<int>& st) {
    visited[u] = true;
    for (auto v : g[u]) {
        if (!visited[v]) dfs1(v, g, visited, st);
    } st.push(u);
}
void dfs2(int u, const vector<vector<int>>& rg, vector<bool>& visited) {
    visited[u] = true;
    for (auto v : rg[u]) if (!visited[v]) dfs2(v, rg, visited);
}
int kosaraju(int n, const vector<vector<int>>& g) {
    vector<bool> visited(n, false); stack<int> st;
    for (int i = 0; i < n; i++) {
        if (!visited[i]) dfs1(i, g, visited, st);
    }
    vector<vector<int>> rg(n);
    for (int u = 0; u < n; u++) for (auto v : g[u]) rg[v].push_back(u);
    fill(visited.begin(), visited.end(), false);
    int scc_count = 0;
    while (!st.empty()) {
        int u = st.top(); st.pop();
        if (!visited[u]) {
            dfs2(u, rg, visited);
            scc_count++;
        }
    } return scc_count;
}

Number
#

ext_euc
#

void ext_euc(int a, int b, int &x, int &y) {
    if (!b) return void(x = 1, y = 0);
    ext_euc(b, a%b, y, x);
    y -= a/b*x;
}

binpow
#

#define int long long
int f(int x, int y) {
    int sum = 1;
    for (; y; y >>= 1) {
        if (y&1) sum *= x;
        x *= x;
    } return sum;
}

矩陣乘法
#

#define vvi vector<vector<int>>
vvi MaT(vvi a, vvi b) {
    vvi c(a.size(), vector<int>(b[0].size()));
    for (int i = 0; i < a.size(); i++)
        for (int j = 0; j < b[0].size(); j++)
            for (int k = 0; k < b.size(); k++)
                c[i][j] += a[i][k] * b[k][j] % p;
    return c;
}

Prime Sieve
#

#define MAXN 1000000
int main() {
    vector<int> p(MAXN, 1), ps;
    p[0] = p[1] = 0;
    for (int i = 2; i < MAXN; i++) {
        if (p[i]) ps.pb(i);
        for (auto j : ps) {
            if (i*j >= p.size()) break;
            p[i*j] = 0;
            if (!(i%j)) break;
        }
    }
}

DP
#

01/Unbounded knapsack
#

int N, W;
vector<int> wt(N), val(N);
vector<int> dp(W+1, 0);
for (int i = 0; i < N; i++) {
    for (int w = W; w >= wt[i]; w--) // 01,每種物品只能拿一次
        dp[w] = max(dp[w], dp[w - wt[i]] + val[i]);
    for (int w = wt[i]; w <= W; w++) // Unbounded,每種物品可重複拿任意多次
        dp[w] = max(dp[w], dp[w - wt[i]] + val[i]);
} cout << dp[W];

多重背包
#

int N, W; // 每種物品最多 cnt[i] 件
vector<int> wt(N), val(N), cnt(N);
vector<int> dp(W+1, 0);

for (int i = 0; i < N; i++) { // 將每個物品拆成若干件 1,2,4,..., 剩餘
    int k = 1, c = cnt[i];
    while (k < c) { // 處理 k 件
        for (int w = W; w >= k * wt[i]; w--)
            dp[w] = max(dp[w], dp[w - k * wt[i]] + k * val[i]);
        c -= k; k <<= 1;
    }
    if (c > 0) for (int w = W; w >= c * wt[i]; w--) // 處理剩餘 c 件
        dp[w] = max(dp[w], dp[w - c * wt[i]] + c * val[i]);
}
cout << dp[W];

稀疏圖:邊較少
稠密圖:邊較多

二分圖匹配
#

#define MAXN1 505
#define MAXN2 505
int n1,n2; // n1 個點連向 n2 個點
int match[MAXN2]; // 屬於 n2 的點匹配了哪個點
vector<int> g[MAXN1]; // 圖 0‐base
bool vis[MAXN2]; // 是否走訪過

bool dfs(int u){
    for(int v : g[u]){
        if(vis[v]) continue;
        vis[v] = 1;
        if(match[v] == -1 || dfs(match[v]))
            return match[v] = u, 1;
    } return 0;
}

int max_match(){
    int ans = 0;
    memset(match, -1, sizeof(int) * n2);
    for(int i = 0; i < n1; ++i){
        memset(vis, 0, sizeof(bool) * n2);
        if(dfs(i)) ++ans;
    } return ans;
}

KM
#

#define MAXN 405
#define INF 0x3f3f3f3f3f3f3f3f
int n, My[MAXN], Mx[MAXN]; // output match, n : 1‐base ,0表示沒有匹配
LL lx[MAXN], ly[MAXN], pa[MAXN], Sy[MAXN], g[MAXN][MAXN]; // g : input graph
bool vx[MAXN], vy[MAXN];
void augment(int y) {
    for (int x, z; y; y = z) {
        x = pa[y], z = Mx[x];
        My[y] = x, Mx[x] = y;
    }
}
void bfs(int st) {
    for (int i = 1; i <= n; ++i) Sy[i] = INF, vx[i] = vy[i] = 0;
    queue<int> q; q.push(st);
    for (;;) {
        while (q.size()) {
            int x = q.front(); q.pop();
            vx[x] = 1;
            for (int y = 1; y <= n; ++y) if (!vy[y]) {
                LL t = lx[x] + ly[y] - g[x][y];
                if (t == 0) {
                    pa[y] = x;
                    if (!My[y]) { augment(y); return; }
                    vy[y] = 1, q.push(My[y]);
                } else if (Sy[y] > t) pa[y] = x, Sy[y] = t;
            }
        }
        LL cut = INF;
        for (int y = 1; y <= n; ++y)
            if (!vy[y] && cut > Sy[y]) cut = Sy[y];
        for (int j = 1; j <= n; ++j) {
            if (vx[j]) lx[j] -= cut;
            if (vy[j]) ly[j] += cut;
            else Sy[j] -= cut;
        }
        for (int y = 1; y <= n; ++y) {
            if (!vy[y] && Sy[y] == 0) {
                if (!My[y]) { augment(y); return; }
                vy[y] = 1, q.push(My[y]);
            }
        }
    }
}

LL KM() {
    memset(My, 0, sizeof(int) * (n + 1));
    memset(Mx, 0, sizeof(int) * (n + 1));
    memset(ly, 0, sizeof(LL) * (n + 1));
    for (int x = 1; x <= n; ++x) {
        lx[x] = -INF;
        for (int y = 1; y <= n; ++y)
            lx[x] = max(lx[x], g[x][y]);
    }
    for (int x = 1; x <= n; ++x) bfs(x);
    LL ans = 0;
    for (int y = 1; y <= n; ++y) ans += g[My[y]][y];
    return ans;
}

LIS
#

int lis(const vector<int>& arr) { // 傳回 arr 序列的 LIS 長度
    int n = arr.size();
    const int INF = 1e9;
    vector<int> d(n + 1, INF);
    d[0] = -INF;
    for (auto i : arr) { // 二分搜尋找第一個 d[l] > arr[i]
        int l = lower_bound(d.begin(), d.end(), i) - d.begin(); // upper_bound 為嚴格遞增
        d[l] = i;
    } // 找出 d 中最大不為 INF 的位置即為 LIS 長度
    int ans = 0;
    for (int l = 0; l <= n; l++) {
        if (d[l] < INF) ans = l;
    } return ans;
}

幾何
#

基礎幾何計算
#

const double PI = atan2(0.0, -1.0);
template <typename T>
struct point {
    T x, y;
    point() {}
    point(const T &x, const T &y) : x(x), y(y) {}
    point operator+(const point &b) const { return point(x + b.x, y + b.y); }
    point operator-(const point &b) const { return point(x - b.x, y - b.y); }
    point operator*(const T &b) const { return point(x * b, y * b); }
    point operator/(const T &b) const { return point(x / b, y / b); }
    bool operator==(const point &b) const { return x == b.x && y == b.y; }
    T dot(const point &b) const { return x * b.x + y * b.y; }
    T cross(const point &b) const { return x * b.y - y * b.x; }
    point normal() const { return point(-y, x); } // 法向量
    T abs2() const { return dot(*this); } // 長度平方
    T rad(const point &b) const { // 兩向量的弧度
        return fabs(atan2(fabs(cross(b)), dot(b)));
    }
    T getA() const { // 對 x 軸的弧度
        T A = atan2(y, x); // 超過 180° 會變負
        if (A <= -PI / 2) A += PI * 2;
        return A;
    }
};

template <typename T>
struct line {
    point<T> p1, p2;
    T a, b, c; // ax + by + c = 0
    line() {}
    line(const point<T> &x, const point<T> &y) : p1(x), p2(y) {}
    void pton() { // 轉成一般式
        a = p1.y - p2.y;
        b = p2.x - p1.x;
        c = -a * p1.x - b * p1.y;
    }
    T ori(const point<T> &p) const { return (p2 - p1).cross(p - p1); }
    T btw(const point<T> &p) const { return (p1 - p).dot(p2 - p); }
    bool point_on_segment(const point<T> &p) const {
        return ori(p) == 0 && btw(p) <= 0;
    }
    T dis2(const point<T> &p, bool is_segment = 0) const {
        point<T> v = p2 - p1, v1 = p - p1;
        if (is_segment) {
            point<T> v2 = p - p2;
            if (v.dot(v1) <= 0) return v1.abs2();
            if (v.dot(v2) >= 0) return v2.abs2();
        }
        T tmp = v.cross(v1);
        return tmp * tmp / v.abs2();
    }
    T seg_dis2(const line<T> &l) const {
        return min({dis2(l.p1, 1), dis2(l.p2, 1), l.dis2(p1, 1), l.dis2(p2, 1)});
    }
    point<T> projection(const point<T> &p) const {
        point<T> n = (p2 - p1).normal();
        return p - n * (p - p1).dot(n) / n.abs2();
    }
    point<T> mirror(const point<T> &p) const {
        point<T> R;
        T d = a * a + b * b;
        R.x = (b * b * p.x - a * a * p.x - 2 * a * b * p.y - 2 * a * c) / d;
        R.y = (a * a * p.y - b * b * p.y - 2 * a * b * p.x - 2 * b * c) / d;
        return R;
    }
    bool equal(const line &l) const { return ori(l.p1) == 0 && ori(l.p2) == 0; }
    bool parallel(const line &l) const { return (p1 - p2).cross(l.p1 - l.p2) == 0; }
    bool cross_seg(const line &l) const {
        return (p2 - p1).cross(l.p1 - p1) * (p2 - p1).cross(l.p2 - p1) <= 0;
    }
    int line_intersect(const line &l) const {
        return parallel(l) ? (ori(l.p1) == 0 ? -1 : 0) : 1;
    }
    int seg_intersect(const line &l) const {
        T c1 = ori(l.p1), c2 = ori(l.p2);
        T c3 = l.ori(p1), c4 = l.ori(p2);
        if (c1 == 0 && c2 == 0) { // 共線
            bool b1 = btw(l.p1) >= 0, b2 = btw(l.p2) >= 0;
            T a3 = l.btw(p1), a4 = l.btw(p2);
            if (b1 && b2 && a3 == 0 && a4 >= 0) return 2;
            if (b1 && b2 && a3 >= 0 && a4 == 0) return 3;
            if (b1 && b2 && a3 >= 0 && a4 >= 0) return 0;
            return -1; // 無限交點
        } else if (c1 * c2 <= 0 && c3 * c4 <= 0) return 1;
        return 0; // 不相交
    }
    point<T> line_intersection(const line &l) const {
        point<T> a = p2 - p1, b = l.p2 - l.p1, s = l.p1 - p1;
        return p1 + a * (s.cross(b) / a.cross(b));
    }
    point<T> seg_intersection(const line &l) const {
        int res = seg_intersect(l);
        if (res <= 0) assert(0);
        if (res == 2) return p1;
        if (res == 3) return p2;
        return line_intersection(l);
    }
};

template <typename T>
struct polygon {
    vector<point<T>> p; // 逆時針順序
    polygon() {}

    T area() const {
        T ans = 0;
        for (int i = p.size() - 1, j = 0; j < (int)p.size(); i = j++)
            ans += p[i].cross(p[j]);
        return ans / 2;
    }

    point<T> center_of_mass() const {
        T cx = 0, cy = 0, w = 0;
        for (int i = p.size() - 1, j = 0; j < (int)p.size(); i = j++) {
            T a = p[i].cross(p[j]);
            cx += (p[i].x + p[j].x) * a;
            cy += (p[i].y + p[j].y) * a;
            w += a;
        }
        return point<T>(cx / 3 / w, cy / 3 / w);
    }
};

template <typename T>
struct triangle {
    point<T> a, b, c;
    triangle() {}
    triangle(const point<T> &a, const point<T> &b, const point<T> &c) : a(a), b(b), c(c) {}
    T area() const {
        T t = (b - a).cross(c - a) / 2;
        return t > 0 ? t : -t;
    }
    point<T> barycenter() const { return (a + b + c) / 3; }
    point<T> circumcenter() const {
        static line<T> u, v;
        u.p1 = (a + b) / 2;
        u.p2 = point<T>(u.p1.x - a.y + b.y, u.p1.y + a.x * b.x);
        v.p1 = (a + c) / 2;
        v.p2 = point<T>(v.p1.x - a.y + c.y, v.p1.y + a.x * c.x);
        return u.line_intersection(v);
    }
    point<T> incenter() const {
        T A = sqrt((b - c).abs2());
        T B = sqrt((a - c).abs2());
        T C = sqrt((a - b).abs2());
        return point<T>(A * a.x + B * b.x + C * c.x,
                        A * a.y + B * b.y + C * c.y) / (A + B + C);
    }
    point<T> perpencenter() const { return barycenter() * 3 - circumcenter() * 2; }
};

凸包
#

struct Point {
    long long x, y;
    Point() {}
    Point(long long x, long long y) : x(x), y(y) {}
    bool operator<(const Point& p) const {
        return x == p.x ? y < p.y : x < p.x;
    }
    Point operator-(const Point& p) const {
        return Point(x - p.x, y - p.y);
    }
};
// 計算兩點向量叉積,判斷方向
long long cross(const Point &a, const Point &b) { return a.x * b.y - a.y * b.x; }
// 凸包求解,傳入點集合points,回傳凸包頂點的vector
vector<Point> convexHull(vector<Point> &points) {
    int n = points.size();
    if (n < 3) return points;  // 點數少於3,全部為凸包頂點
    sort(points.begin(), points.end());  // 按 x, y 排序
    vector<Point> hull(2 * n); int k = 0;
    for (int i = 0; i < n; i++) { // 求下凸包
        while (k >= 2 && cross(hull[k - 1] - hull[k - 2], points[i] - hull[k - 2]) <= 0)
            k--;
        hull[k++] = points[i];
    }
    for (int i = n - 2, t = k + 1; i >= 0; i--) { // 求上凸包
        while (k >= t && cross(hull[k - 1] - hull[k - 2], points[i] - hull[k - 2]) <= 0)
            k--;
        hull[k++] = points[i];
    }
    hull.resize(k - 1);  // 最後一點和第一點重合,去重
    return hull;
}

平面最近點對
#

#define LL long long int
#define INF 9223372036854775807
#define x first
#define y second
LL n;
pair<LL,LL> tmp[200001];
vector<pair<LL,LL>> p;
LL dis(pair<LL,LL> a,pair<LL,LL> b){
    LL dx = a.x - b.x, dy = a.y - b.y;
    return dx * dx + dy * dy;
}
bool cmp(pair<LL,LL> a, pair<LL,LL> b) { return a.y < b.y; }
#define mid (l + r) / 2
LL divide(LL l, LL r){
    if(l == r) return INF;
    LL m = (l + r) / 2, ans = min(divide(l, mid), divide(mid+1, r)), midval = p[m].x, pp = -1;
    for(LL i = l; i <= r; i++)
        if ((midval - p[i].x)*(midval - p[i].x) <= ans)
            tmp[++pp] = p[i];
    sort(tmp, tmp+pp+1, cmp);
    for(LL i = 0; i < pp+1; i++){
        for(LL j = i+1; j < pp+1; j++){
            ans = min(ans, dis(tmp[i], tmp[j]));
            if((tmp[i].y - tmp[j].y)*(tmp[i].y - tmp[j].y) > ans) break;
        }
    } return ans;
}
#undef mid
int main(){
    cin >> n;
    p.assign(n, {0,0});
    for (int i = 0; i < n; i++) cin >> p[i].x >> p[i].y;
    sort(p.begin(), p.end());
    cout << divide(0, n-1) << '\n';
}

鞋帶公式
#

double polygon_area(const vector<pair<double, double>> &pts) {
    int n = pts.size(); double area = 0;
    for (int i = 0; i < n; ++i) {
        int j = (i + 1) % n;
        area += pts[i].first * pts[j].second - pts[j].first * pts[i].second;
    }
    return abs(area) / 2.0;
}

編譯 & 運行程式碼
#

C++
#

g++ -std=c++17 -O2 -Wall main.cpp -o main
./main < test.in > my.out

Python
#

python3 main.py < test.in > my.out

比較輸入輸出
#

diff -w my.out test.out
Piau 的筆記本
作者
Piau 的筆記本
希望我寫下來的東西能夠長久的記在我的腦中