模板汇总

也许是中学生涯最后一次打板子了呀...


KMP模式匹配

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 1000005;
char a[N], b[N];
int a1, b1, Next[N];
int main(){
    cin >> a + 1;
    cin >> b + 1;
    a1 = strlen(a + 1);
    b1 = strlen(b + 1);
    Next[1] = 0;
    for(int i = 2, j = 0; i <= b1; i++){
        while( j > 0 && b[j + 1] != b[i] ) j = Next[j];
        if( b[i] == b[j + 1] ) j++;
        Next[i] = j;
    }
    for(int i = 1, j = 0; i <= a1; i++){
        while( j > 0 && a[i] != b[j + 1]) j = Next[j];
        if(a[i] == b[j + 1]) j++;
        if(j == b1) {
            cout << i - j + 1 <<"\n";
            j = Next[j];
        }
    }
    for(int i = 1; i <= b1; i++) cout << Next[i] <<" ";
    return 0;
} 

Trie字典树

#include<iostream>
#include<cstdio>
using namespace std;
int Trie[N][27], end[p] = 1, tot = 1;
void insert(char* str){
    int len = strlen(str), p = 1;
    for(int k = 0; k < len; k++){
        int ch = str[k] - 'a';
        if(trie[p][ch] == 0) trie[p][ch] = ++tot;
        p = trie[p][ch];
    }
    end[p] = 1;
}
bool search(char* str){
    int len = strlen(str), p = 1;
    for(int k = 0; k < len; k++){
        p = trie[p][str[k]];
        if(p == 0) return 0;
    }
    return end[p];
}

ST表

值得注意的是,log函数最好配强制类型转换(不然poj会编译错误),转换的时候要用括号把log全部括一下,初始化的时候k要加一。初始化的时候j从0开始

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int N = 100005;
int a[N][30], n, m, k;
int read(){
    int x = 0, ch = getchar();
    while(ch < '0' || ch > '9') ch = getchar();
    while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
    return x;
}
int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i++) a[i][0] = read();
    k = int(log(n) / log(2) + 1);
    for(int i = 1; i <= k; i++)
        for(int j = 0; j <= n - (1 << i) + 1; j++)
            a[j][i] = max(a[j][i - 1], a[j + (1 << (i-1))][i - 1]);
    while(m--){
        int x = read(), y = read();
        k = int(log(y - x + 1) / log(2));
        cout << max(a[x][k], a[y - (1 << k) + 1][k]) << "\n";
    }
    return 0;
}

单源最短路径 \(dijkstra\)

注意打标记和判重都在取出队头的时候进行

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N = 500005;
int ver[N], head[N], Next[N], edge[N], tot;
int n, m, s;
void add(int x, int y, int z){
    ver[++tot] = y, edge[tot] = z;
    Next[tot] = head[x], head[x] = tot;
}
int read(){
    int x = 0, ch = getchar();
    while(ch < '0' || ch > '9') ch = getchar();
    while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
    return x;
}

/*Dijkstra*/
int d[N], v[N];
void dijkstra(int s){
    memset(d, 0x3f, sizeof d);
    memset(v, 0, sizeof v);
    priority_queue< pair< int, int > > q;
    q.push(make_pair(0, s)); d[s] = 0;
    while(q.size()){
        int x = q.top().second; q.pop();
        if(v[x]) continue;
        v[x] = 1;
        for(int i = head[x]; i; i = Next[i]){
            int y = ver[i], z = edge[i];
            if(d[y] > d[x] + z){
                d[y] = d[x] + z;
                q.push(make_pair(-d[y], y));
            }
        }
    }
}
int main(){
    cin >> n >> m >> s;
    for(int i = 1; i <= m; i++){
        int x = read(), y = read(), z = read();
        add(x, y, z);
    }
    dijkstra(s);
    for(int i = 1; i <= n; i++){
        if(d[i] == 0x3f3f3f3f)
            cout << "2147483647 ";
        else cout << d[i] <<" ";
    }
    return 0;
}

单源最短路径\(SPFA\)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N = 500005;
int ver[N], head[N], Next[N], edge[N], tot;
int n, m, s;
void add(int x, int y, int z){
    ver[++tot] = y, edge[tot] = z;
    Next[tot] = head[x], head[x] = tot;
}
int read(){
    int x = 0, ch = getchar();
    while(ch < '0' || ch > '9') ch = getchar();
    while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
    return x;
}
int d[N], v[N];
/* spfa */
void spfa(int s){
    memset(d, 0x3f, sizeof d);
    memset(v, 0, sizeof v);
    queue<int> q;
    q.push(s); v[s] = 1; d[s] = 0;
    while(q.size()){
        int x = q.front(); q.pop();
        v[x] = 0;
        for(int i = head[x]; i; i = Next[i]){
            int y = ver[i], z = edge[i];
            if(d[y] > d[x] + z){
                d[y] = d[x] + z;
                if(!v[x]) q.push(y), v[y] = 1;
            }
        }
    }
}
int main(){
    cin >> n >> m >> s;
    for(int i = 1; i <= m; i++){
        int x = read(), y = read(), z = read();
        add(x, y, z);
    }
    spfa(s);
    for(int i = 1; i <= n; i++){
        if(d[i] == 0x3f3f3f3f)
            cout << "2147483647 ";
        else cout << d[i] <<" ";
    }
    return 0;
}

扩展欧几里得

\(x = \frac{c}{d}x_0 + k \frac{b}{d}, y = \frac {c}{d}y_0 - k \frac{a}{d}\)

#include<iostream>
#define ll long long
using namespace std;
int exgcd1(int a, int b, int &x, int &y){
    if(b == 0) {
        x = 1, y = 0;
        return a;
    }
    int d = exgcd1(b, a % b, x, y);
    int z = x; x = y, y = z - (a / b) * y;
    return d;
}
ll exgcd(ll a, ll b, ll &x, ll &y){
    if(b == 0){
        x = 1, y = 0;
        return a;
    }
    int d = exgcd(b, a % b, x, y);
    int z = x; x = y, y = z - (a / b) * y;
    return d;
}
int main(){
    ll a, b, x, y, d;
    cin >> a >> b;
    d = exgcd(a, b, x, y);
    cout << (x % (b / d) + (b / d)) % (b / d) <<"\n";
    return 0;
}

最近公共祖先 LCA

  • 树上倍增法
    敲这个板子出了一堆错误。所以要记得空间开到两倍,lca的大于等于号。
#include<iostream>
#include<cstdio>
#include<queue>
#include<cmath>
#include<algorithm>
using namespace std;
const int N = 1000005;
int ver[N], head[N], Next[N], tot;
int n, m, s, k, f[N][25], d[N];
void add(int x, int y){
    ver[++tot] = y, Next[tot] = head[x], head[x] = tot;
}
void bfs(){
    queue<int> q;
    q.push(s); d[s] = 1;
    while(q.size()){
        int x = q.front(); q.pop();
        for(int i = head[x]; i; i = Next[i]){
            int y = ver[i];
            if(d[y]) continue;
            d[y] = d[x] + 1;
            f[y][0] = x;
            for(int j = 1; j <= k; j++)
                f[y][j] = f[f[y][j - 1]][j - 1];
            q.push(y);
        }
    }
}
int lca(int x, int y){
    if(d[y] < d[x]) swap(x, y);
    for(int i = k; i >= 0; i--)
        if(d[f[y][i]] >= d[x]) y = f[y][i];
    if(x == y) return x;
    for(int i = k; i >= 0; i--)
        if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
    return f[x][0];
}

int read(){
    int x = 0, ch = getchar();
    while(ch < '0' || ch > '9') ch = getchar();
    while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
    return x;
}
int main(){
    cin >> n >> m >> s;
    k = (int)( log(n) / log(2) ) + 1;
    for(int i = 1; i < n; i++) {
        int x = read(), y = read();
        add(x, y), add(y, x);
    }
    bfs();
    while(m--){
        int x = read(), y = read();
        cout << lca(x, y) <<"\n";
    }

    return 0;
}

  • 树链剖分法
#include<iostream>
#include<cstdio>
using namespace std;
const int N = 1000005;
int ver[N], head[N], Next[N], tot;
int d[N], son[N], f[N], top[N], v[N], size[N];
int n, m, s;
void add(int x, int y){
    ver[++tot] = y, Next[tot] = head[x], head[x] = tot;
}

void dfs1(int x){
    v[x] = 1;
    size[x] = 1;
    for(int i = head[x]; i; i = Next[i]){
        int y = ver[i];
        if(v[y]) continue;
        d[y] = d[x] + 1;
        dfs1(y);
        f[y] = x;
        size[x] += size[y];
        if(!son[x] || size[y] > size[son[x]])
            son[x] = y;
    }
}
void dfs2(int x, int p){
    top[x] = p;
    if(son[x]) dfs2(son[x], p);
    for(int i = head[x]; i; i = Next[i]){
        int y = ver[i];
        if(y == f[x] || y == son[x]) continue;
        dfs2(y, y);
    }
}
int lca(int x, int y){
    while(top[x] != top[y])
        if(d[top[x]] > d[top[y]]) x = f[top[x]];
        else y = f[top[y]];
    if(d[x] > d[y]) return y;
    return x;
}
int read(){
    int x = 0, ch = getchar();
    while(ch < '0' || ch > '9') ch = getchar();
    while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
    return x;
}
int main(){
    cin >> n >> m >> s;
    for(int i = 1; i < n; i++) {
        int x = read(), y = read();
        add(x, y), add(y, x);
    }
    d[s] = 1;
    dfs1(s);
    dfs2(s, s);
    while(m--){
        int x = read(), y = read();
        cout << lca(x, y) <<"\n";
    }
    return 0;
}

最小生成树 Kruskal

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 200005;
int n, m, f[N], ans = 0;
struct node{
    int x, y, z;
}edge[N];
bool cmp(node a, node b){
    return a.z < b.z;
}
int get(int x){
    if(x == f[x]) return x;
    return f[x] = get(f[x]);
}
int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i++) f[i] = i;
    for(int i = 1; i <= m; i++){
        cin >> edge[i].x >> edge[i].y >> edge[i].z;
    }
    sort(edge + 1, edge + m + 1, cmp);
    for(int i = 1; i <= m; i++){
        int x = edge[i].x, y = edge[i].y;
        x = get(x), y = get(y);
        if(x == y) continue;
        f[x] = y;
        ans += edge[i].z;
    }
    cout << ans;
    return 0;
}

树的直径

void dp(int x){
    v[x] = 1;
    for(int i = head[x]; i; i = Next[i]){
        int y = ver[i];
        if(v[y]) continue;
        dp(y);
        ans = max(ans, d[x] + d[y] + edge[i]);
        d[x] = max(d[x], d[y] + edge[i]);
    }
}

树状数组

OI生涯学的第一个数据结构!
板子超好写,难在分析题目中能用前缀和、差分解决的地方。

void update(int x, int y){
    for(; x <= n; x += x & -x)
        s[x] += y;
}
int getsum(int x){
    int ans = 0;
    for(; x; x -= x & -x)
        ans += s[x];
    return ans;
}

归并排序

\(mid - i + 1\)

#include<iostream>
using namespace std;
int a[100005], b[100005], ans;
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 = l;
    while(i <= mid || j <= r){
        if(j > r || ( a[i] <= a[j] && i <= mid ))
            b[k++] = a[i++];
        else
            b[k++] = a[j++], ans += mid - i + 1;
    }
    for(int i = l; i <= r; i++)
        a[i] = b[i];
}
int main(){
    int n;
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];
    merge_sort(1, n);
    cout << ans;
    return 0;
}

乘法逆元

#include<iostream>
#include<cstdio>
using namespace std;
const int N = 3000005;
long long a[N], n, p;
int main(){
    cin >> n >> p;
    a[0] = a[1] = 1;
    cout << 1 <<"\n";
    for(int i = 2; i <= n; i++){
        a[i] = (p - p / i) * a[p % i] % p;
        cout << a[i] <<"\n";
    }

    return 0;
}

卢卡斯定理

#include<iostream>
#include<cstdio>
using namespace std;
const int N = 1000005;
long long a[N];
long long n, m, p;

long long fp(long long a, long long b){
    long long ans = 1;
    for(; b; b >>= 1){
        if(b & 1) ans = ans * a % p;
        a = a * a % p;
    }

    return ans;
}


long long C(long long n, long long m){
    if(n < m) return 0;
    return (a[n] * fp(a[m], p - 2)) % p * fp(a[n - m], p - 2) % p ;
}
long long Lucas(long long n, long long m){
    if(m == 0) return 1;
    return C(n % p, m % p) * Lucas(n / p, m / p) % p;
}

int read(){
    int x = 0, ch = getchar();
    while(ch < '0' || ch > '9') ch = getchar();
    while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
    return x;
}



int main(){
//  for(int i = 2; i < p; i++) a[i] = (p - p / i) * a[p % i] % p;

    int t = read();
    while(t--){
        n = read(), m = read(), p = read();
        a[0] = 1;
        for(int i = 1; i <= p; i++) a[i] = a[i - 1] * i % p;
        cout << Lucas(n + m, n) <<"\n";
    }


    return 0;
}

线段树

一个暑假的记忆,大概是我最有把握的模板了吧。

#include<iostream>
#include<cstdio>
using namespace std;
const int SIZE = 100005;
struct SegmentTree{
    int l, r;
    long long sum, add;
}t[SIZE * 4];
int a[SIZE];
void build(int p, int l, int r){
    t[p].l = l, t[p].r = r;
    if(l == r) {
        t[p].sum = a[l];
        return ;
    }
    int mid = (l + r) >> 1;
    build(p*2, l, mid);
    build(p*2+1, mid + 1, r);
    t[p].sum = t[p*2].sum + t[p*2+1].sum;
}
void spread(int p){
    if(t[p].add){
        t[p*2].sum += (long long)(t[p*2].r - t[p*2].l + 1) * t[p].add;
        t[p*2+1].sum += (long long)(t[p*2+1].r - t[p*2+1].l + 1) * t[p].add;
        t[p*2].add += t[p].add;
        t[p*2+1].add += t[p].add;
        t[p].add = 0;
    }
}
void change(int p, int l, int r, int val){
    if(l <= t[p].l && r >= t[p].r){
        t[p].add += val;
        t[p].sum += (long long)(t[p].r - t[p].l + 1) * val;
        return;
    }
    spread(p);
    int mid = (t[p].l + t[p].r) >> 1;
    if(l <= mid) change(p*2, l, r, val);
    if(r > mid) change(p*2+1, l, r, val);
    t[p].sum = t[p*2].sum + t[p*2+1].sum;
}
long long ask(int p, int l, int r){
    if(l <= t[p].l && r >= t[p].r) return t[p].sum;
    spread(p);
    int mid = (t[p].l + t[p].r) >> 1;
    long long val = 0;
    if(l <= mid) val += ask(p*2, l, r);
    if(r > mid) val += ask(p*2+1, l, r);
    return val;
}
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 f, x, y, h;
        cin >> f >> x>> y;
        if(f == 1){
            cin >> h;
            change(1, x, y, h);
        }
        if(f == 2){
            cout << ask(1, x, y) <<"\n";
        }
    }
    return 0;
}

并查集

板子不重要,路径压缩和带权才重要

int get(int x){
    if(x == fa[x]) return x;
    return fa[x] = get(fa[x]);
}
void merge(int x, int y){
    fa[get(x)] = get(y);
}

平衡树Treap

#include<iostream>
#include<cstdlib>
using namespace std;
const int SIZE = 100005;
struct Treap{
    int l, r;
    int val, dat;
    int size, cnt;
}a[SIZE];
int tot, root, n, INF=0x7fffffff;

int New(int val){
    a[++tot].val = val;
    a[tot].dat = rand();
    a[tot].size = a[tot].cnt = 1;
    return tot;
}

void Update(int p){
    a[p].size = a[a[p].l].size + a[a[p].r].size + a[p].cnt;
}

void Build(){
    New(-INF), New(INF);
    root = 1, a[1].r = 2;
    Update(root);
}

void zig(int &p){
    int q = a[p].l;
    a[p].l = a[q].r, a[q].r = p, p = q;
    Update(a[p].r), Update(p);
}

void zag(int &p){
    int q = a[p].r;
    a[p].r = a[q].l, a[q].l = p, p = q;
    Update(a[p].l), Update(p);
}

void Insert(int &p, int val){
    if(p == 0){
        p = New(val);
        return;
    }
    if(a[p].val == val){
        a[p].cnt++, Update(p);
        return;
    }
    if(val < a[p].val){
        Insert(a[p].l, val);
        if(a[p].dat < a[a[p].l].dat) zig(p);
    }
    else{
        Insert(a[p].r, val);
        if(a[p].dat < a[a[p].r].dat) zag(p);
    }
    Update(p);
}

int GetRankByVal(int p, int val){
    if(p == 0) return 0;
    if(val == a[p].val) return a[a[p].l].size;
    if(val < a[p].val) return GetRankByVal(a[p].l, val);
    return a[a[p].l].size + a[p].cnt + GetRankByVal(a[p].r, val);
}

int GetValByRank(int p, int rank){
    if(p == 0) return INF;
    if(a[a[p].l].size >= rank) return GetValByRank(a[p].l, rank);
    if(a[a[p].l].size + a[p].cnt >= rank) return a[p].val;
    return GetValByRank(a[p].r, rank - a[a[p].l].size - a[p].cnt);
}

int GetPrev(int val){
    int ans = 1;
    int p = root;
    while(p){
        if(val == a[p].val){
            if(a[p].l > 0){
                p = a[p].l;
                while(a[p].r > 0) p = a[p].r;
                ans = p;
            }
            break;
        }
        if(a[ans].val < a[p].val && a[p].val < val) ans = p;
        p = val < a[p].val ? a[p].l : a[p].r;
    }
    return a[ans].val;
}
int GetNext(int val){
    int ans = 2;
    int p = root;
    while(p){
        if(val == a[p].val){
            if(a[p].r > 0){
                p = a[p].r;
                while(a[p].l > 0) p = a[p].l;
                ans = p;
            }
            break;
        }
        if(a[ans].val > a[p].val && a[p].val > val) ans = p;
        p = val < a[p].val ? a[p].l : a[p].r;
    }
    return a[ans].val;
}

void Remove(int &p, int val){
    if(p == 0) return;
    if(val == a[p].val){
        if(a[p].cnt > 1){
            a[p].cnt--, Update(p);
            return;
        }
        if(a[p].l || a[p].r){
            if(a[p].r == 0 && a[a[p].l].dat > a[a[p].r].dat)
                zig(p), Remove(a[p].r, val);
            else
                zag(p), Remove(a[p].l, val);
            Update(p);
        }else p = 0;
        return;
    }
    val < a[p].val ? Remove(a[p].l, val) : Remove(a[p].r, val);
    Update(p);
}
int main(){
    Build();
    cin >> n;
    while(n--){
        int opt, x;
        cin >> opt >> x;
        switch(opt){
            case 1:
                Insert(root, x);
                break;
            case 2:
                Remove(root, x);
                break;
            case 3:
                cout << GetRankByVal(root, x) - 1 <<"\n";
                break;
            case 4:
                cout << GetValByRank(root, x+1) <<"\n";
                break;
            case 5:
                cout << GetPrev(x) <<"\n";
                break;
            case 6:
                cout << GetNext(x) <<"\n";
                break;
        }
    }
    return 0;
}

平衡树Splay

#include<iostream>
using namespace std;

const int _ = 100005;
const int INF = 0x7fffffff;

int n, opt, x;

struct Spaly{
    int root, tot, cnt[_], val[_], size[_], ch[_][2], fa[_];

    bool chk(int x){
        return ch[fa[x]][1] == x;
    }

    void pushup(int x){
        if(!x) return;
        size[x] = size[ch[x][0]] + size[ch[x][1]] + cnt[x];
    }

    int newnode(int x){
        val[++tot] = x;
        cnt[tot] = size[tot] = 1;
        ch[x][0] = ch[x][1] = 0;
        return tot;
    }

    void rotate(int x){
        int y = fa[x], z = fa[y], k = chk(x), w = ch[x][k^1];
        ch[y][k] = w, fa[w] = y;
        ch[z][chk(y)] = x, fa[x] = z;
        ch[x][k^1] = y, fa[y] = x;
        pushup(y), pushup(x);
    }

    void splay(int x, int goal = 0){
        while(fa[x] != goal){
            int y = fa[x], z = fa[y];
            if(z != goal){
                if(chk(x) == chk(y)) rotate(y);
                else rotate(x);
            }
            rotate(x);
        }
        if(!goal) root = x;
    }

    void insert(int x){
        int cur = root, p = 0;
        while(cur && x != val[cur])
            p = cur, cur = ch[cur][x > val[cur]];
        if(cur) ++cnt[cur], splay(cur);
        else cur = ch[p][x > val[p]] = newnode(x), fa[cur] = p, splay(cur);
    }

    void find(int x){
        if(!root) return;
        int cur = root;
        while(ch[cur][x > val[cur]] && x != val[cur]) cur = ch[cur][x > val[cur]];
        splay(cur);
    }

    int rank(int x){
        find(x);
        return cnt[ch[root][0]] + 1;
    }

    int kth(int k){
        int cur = root;
        while(1){
            if(ch[cur][0] && k <= size[ch[cur][0]]) cur = ch[cur][0];
            else if(k > size[ch[cur][0]] + cnt[cur]) k -= size[ch[cur][0]] + cnt[cur], cur = ch[cur][1];
            else return cur;
        }
    }

    int pre(int x){
        find(x);
        if(val[root] < x) return root;
        int cur = ch[root][0];
        while(ch[cur][1]) cur = ch[cur][1];
        return cur;
    }

    int next(int x){
        find(x);
        if(val[root] > x) return root;
        int cur = ch[root][1];
        while(ch[cur][0]) cur = ch[cur][0];
        return cur;
    }

    void remove(int x){
        int u = pre(x), v = next(x);
        splay(u), splay(v, u);
        int w = ch[v][0];
        if(cnt[w] > 1) --cnt[w], splay(w);
        else size[0] = cnt[0] = ch[v][0] = 0;
    }

    void maintain(int x){
        if(!ch[x][0] && !ch[x][1]) size[x] = cnt[x];
        if(ch[x][0]) maintain(ch[x][0]);
        if(ch[x][1]) maintain(ch[x][1]);
        pushup(x);
    }

}T;

int main(){
    T.insert(-INF), T.insert(INF);
    cin >> n;
    for(int i=1; i<=n; i++){
        cin >> opt >> x;
        if(opt == 1) T.insert(x);
        if(opt == 2) T.remove(x);
        if(opt == 3) cout << T.rank(x) - 1 <<"\n";
        if(opt == 4) cout << T.val[T.kth(x)+1] <<"\n";
        if(opt == 5) cout << T.val[T.pre(x)] <<"\n";
        if(opt == 6) cout << T.val[T.next(x)] <<"\n";
    }

    return 0;
}
01-12 02:00