ACM 算法模板

本文总结了一系列常用算法模板,包括匈牙利算法、并查集、ST表等数据结构类算法,Manacher、KMP等字符串类算法,SPFA、Dijkstra等图论类算法,以及快速幂、矩阵快速幂等数学类算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


3、匈牙利算法
/*
 * 匈牙利算法
 * 默认给定二分图
 */
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
#define map mmap
#define search ssearch
#define maxn (1010)
int n, m, map[maxn][maxn], from[maxn], vis[maxn];
bool search(int u)
{
    for(int v = 1; v <= n; v++) if(map[u][v] && !vis[v])
    {
        vis[v] = 1;
        if(!from[v] || search(from[v]))
        {
            from[v] = u;
            from[u] = v;
            return true;
        }
    }
    return false;
}
int main()
{
    cin >> n >> m;
    for(int i = 0, u, v; i < m; i++)
    {
        scanf("%d%d", &u, &v);
        map[u][v] = map[v][u] = 1;
    }
    int tot = 0;
    for(int i = 1; i <= n; i++) if(!from[i])
    {
        memset(vis, 0, sizeof(vis));
        if(search(i)) tot++;
    }
    cout << tot << endl;
    return 0;
}

ACM 算法模板


一、数据结构类


1、并查集
/*
 * 并查集
 * 判断图连通性,速度快过宽搜很多
 */
int father[250010 * 2], rank[250010 * 2];
void disjoint_set(int n)
{
    for(int i = 1; i <= n; i++)
        father[i] = i;
}
int find(int v)
{
    return father[v] = father[v] == v? v: find(father[v]);
}
void merge(int x, int y)
{
    int a = find(x), b = find(y);
    if(rank[a] < rank[b])
        father[a] = b;
    else
    {
        father[b] = a;
        if(rank[a] == rank[b])
            rank[a]++;
    }
}

2、st表
/*
 * st表
 * st[i][j]表示元素arr[i]开始,长度为2^j区间内的最值
 * st[i][0]为arr[i],st[i][j] = max(st[i][j-1], st[i - (1 << j-1)][j-1])
 * 求区间[l, r]内最值:
 * log2[i]代表i的对数向下取整
 * 对于长度len而言,显然2^log2[len]严格大于len的一半
 * 令k = log2[r - l + 1],则最值为max(st[l][k], st[r - (1<<k) + 1][k])
 */
#define maxn (55000)
int log2[maxn], st[maxn][32];
void st_prepare(int n, int *arr)
{
    log2[1] = 0;
    for(int i = 2; i <= n; i++)
    {
        log2[i] = log2[i - 1];
        if((1 << log2[i] + 1) == i)
            log2[i]++;
    }

    for(int i = n; i >= 1; i--)
    {
        st[i][0] = arr[i];
        for(int j = 1; i + (1 << j) - 1 <= n; j++)
        {
            st[i][j] = max(st[i][j - 1], st[i + (1 << j - 1)][j - 1]);
        }
    }
}
int st_query(int l, int r)
{
    int len = r - l + 1, k = log2[len];
    return max(st[l][k], st[r - (1 << k) + 1][k]);
}

3、树状数组 & 二维树状数组

树状数组

/*
 * 树状数组
 * 可对A[x]增加value,可对A[1]~A[x]区间求和
 * 相比线段树,树状数组最大的优势是节约区间(线段树一般要开4倍规模)
 */
int seg[maxn];
inline int lowbit(int x)
{
    return (x&-x);
}
void add(int x, int value, int n)
{
    for(int i = x; i <= n; i += lowbit(i))
    {
        seg[i] += value;
    }
}
int get(int x)
{
    int sum = 0;
    for(int i = x; i; i -= lowbit(i))
        sum += seg[i];
    return sum;
}

二维树状数组

/*
 * 二维树状数组
 * 可对c[x][y]增加value,可对c[1][1]到c[x][y]矩阵求和
 */
int c[maxn][maxn];
inline int lowbit(int x)
{
    return (x&-x);
}
void add(int x, int y, int value, int n)
{
    for(int i = x; i <= n; i += lowbit(i))
    {
        for(int j = y; j <= n; j += lowbit(j))
        c[i][j] += value;
    }
}
int get(int x, int y)
{
    int sum = 0;
    for(int i = x; i; i -= lowbit(i))
    {
        for(int j = y; j; j -= lowbit(j))
        sum += c[i][j];
    }
    return sum;
}

4、线段树 & 树链剖分
/*
 * 基于dfs的树链剖分 & 线段树
 */
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
#define maxn (10010)
struct edge
{
    int a, b, c;
} e[maxn];
vector<int> son[maxn];
int fa[maxn], dep[maxn], sz[maxn], next[maxn];
void dfs1(int u, int f, int d)
{
    fa[u] = f;
    dep[u] = d;
    sz[u] = 1;
    next[u] = 0;
    for(int i = 0, v; i < son[u].size(); i++)
    if((v = son[u][i]) != f) {
        dfs1(v, u, d + 1);
        sz[u] += sz[v];
        if(sz[next[u]] < sz[v])
            next[u] = v;
    }
}
int top[maxn], w[maxn], idx[maxn], cnt;
void dfs2(int u, int tp)
{
    top[u] = tp;
    idx[u] = ++cnt;
    if(!next[u]) return ;
    dfs2(next[u], tp);
    for(int i = 0, v; i < son[u].size(); i++)
    if((v = son[u][i]) != fa[u] && v != next[u]) {
        dfs2(v, v);
    }
}
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
int seg[maxn << 2];
void build(int l, int r, int rt)
{
    if(l == r) { seg[rt] = w[l]; return; }
    int m = (l + r) >> 1;
    build(lson), build(rson);
    seg[rt] = max(seg[rt<<1], seg[rt<<1|1]);
}
void update(int x, int val, int l, int r, int rt)
{
    if(l == r) { seg[rt] = val; return; }
    int m = (l + r) >> 1;
    if(x <= m) update(x, val, lson);
    else update(x, val, rson);
    seg[rt] = max(seg[rt<<1], seg[rt<<1|1]);
}
int query(int L, int R, int l, int r, int rt)
{
    if(L <= l && r <= R) return seg[rt];
    int m = (l + r) >> 1, ret = -1;
    if(L <= m) ret = max(ret, query(L, R, lson));
    if(R > m) ret = max(ret, query(L, R, rson));
    return ret;
}
int qtree(int u, int v, int n)
{
    int topu = top[u], topv = top[v], ans = 0;
    while(topu != topv)
    {
        // printf("while()\n");
        if(dep[topu] < dep[topv]) { swap(topu, topv); swap(u, v); }
        ans = max(query(idx[topu], idx[u], 1, n, 1), ans);
        u = fa[topu];
        topu = top[u];
    }
    if(u == v) return ans;
    if(dep[u] > dep[v]) swap(u, v);
    ans = max(ans, query(idx[next[u]], idx[v], 1, n, 1));
    return ans;
}

5、主席树
/*
 * 主席树
 * 可持久化线段树
 * T[0]预留,作为“底板”,也就是版本0
 */
#define maxn (100010)
int root[maxn], cnt;
struct seg
{
    int lson, rson, num;
} T[maxn * 40];

void update(int l, int r, int &now, int pre, int a)
{
    now = ++cnt;
    T[now] = T[pre];
    T[now].num++;
    if(l == r) return ;
    int mid = (l + r) >> 1;
    if(a <= mid) update(l, mid, T[now].lson, T[pre].lson, a);
    else update(mid + 1, r, T[now].rson, T[pre].rson, a);
}
int query(int l, int r, int pre, int now, int k)
{
    if(l == r) { return l; }
    int mid = (l + r) >> 1, lnum = T[T[now].lson].num - T[T[pre].lson].num;
    if(k <= lnum)
        return query(l, mid, T[pre].lson, T[now].lson, k);
    else
        return query(mid + 1, r, T[pre].rson, T[now].rson, k - lnum);
}

二、字符串类


0、字符串哈希
/*
 * 自然溢出字符串哈希
 * 可用于字符串判重,或利用字符串单调性结合二分解决问题
 */
typedef unsigned long long ulint;
const ulint seed = 50009uLL;
#define maxn (100010)
ulint xp[maxn], H[maxn];
char s[maxn];
void init_xp(int n)
{
    xp[0] = 1;
    for(int i = 1; i < n; i++)
        xp[i] = xp[i-1] * seed;
}
void init_hash(int n)
{
    H[0] = s[0] - 'a' + 1;
    for(int i = 1; i < n; i++)
        H[i] = H[i-1] * seed + (ulint)(s[i] - 'a' + 1);
}
void ask_hash(int l, int r)
{
    if(l == 0) return H[r];
    return H[r] - H[l-1] * xp[r - l + 1];
}

1、manacher算法
void manacher(string& s, int *R, int n)
{
/*
 * manacher算法
 * 需要将字符串预处理成$#x#x#x#x#x#x#形式
 * 若仅求长度为奇数的回文串,最左侧添加特殊字符即可
 * 记录当前最右延伸回文半径mx和对应回文中心p
 * i若位于mx以内,则将对称位置2*p-i的回文半径的不越界部分作为i的回文半径,并且继续向右侧匹配
 * 若得到新的最右延伸回文半径,更新mx和p
 */
    int p = 0, mx = 0;
    R[0] = 1;
    for(int i = 1; i < n; i++)
    {
        if(mx > i)
            R[i] = min(R[2*p - i], mx - i);
        else R[i] = 1;
        while(s[i - R[i]] == s[i + R[i]])
            R[i]++;
        if(i + R[i] > mx)
            p = i, mx = i + R[i];
    }
    return;
}

2、kmp算法
#define maxn (300000)
int next[maxn];
vector<int> pos;
void kmp(string s1, string s2)
{
/*
 * kmp实质:对模式串的每个前缀,求出与之后缀匹配的最长真前缀长度
 * next[i]表示文本串s2在匹配样式s1[i]时首次失配后的新s1下标
 * next[i]也是与s1[i-1](已匹配部分)后缀相匹配的最长真前缀长度
 * 以上注意i要大于0,next[0]没有意义
 * 与s1[i]后缀匹配的最长真前缀长度为next[i+1]
 * 所以第一个for循环也是求s1[i]最长真前缀的过程
 */
    // step 1
    int n = s1.size();
    memset(next, 0, sizeof(int) * (n + 1));
    for(int i = 1, j; i < n; i++)
    {
        j = i; //从j处开始试配
        while(j > 0) //第一次手动失配,相当于模拟s2的失配情况
        {
            j = next[j];
            if(pattern[i] == pattern[j])
            {
                next[i + 1] = j + 1; //如果在i+1处失配,只需要从j+1处开始匹配
                break;  // next[i + 1] 为s1[i]最长真前缀长度
            }
        }
    }
    // step 2
    int m = s2.size();
    pos.clear();
    for(int i = 0, j = 0; i < m; i++)
    {
        if(s2[i] == s1[j])
        {
            j++;
        }
        else
        {
            while(j > 0) // j最多回退至0
            {
                j = next[j];
                if(s1[j] == s2[i])
                {
                    j++;
                    break;
                }
            }
        }
        if(j == n) pos.push_back(i - n + 1); // 注意s1 s2应以'\0'结尾
    }
}

3、Trie树

/*
 * Trie树
 * 一般情况下出度为26,可搜索
 * 若需要对相同字符串的插入做不同记录(比如不同id),将ed换为vector即可
 * 一个典型的用法是将数字以二进制插入trie树中,方便做异或操作
 */
#define maxn (1000000)
struct Trie
{

    int next[maxn][26], ed[maxn];
    int L, root;
    int newnode()
    {
        for(int i = 0; i < 26; i++)
            next[L][i] = -1;
        ed[L] = 0;
        return L++;
    }
    void init()
    {
        L = 0;
        root = newnode();
    }
    void insert(char s[])
    {
        int now = root;
        for(int i = 0, sz = strlen(s); i < sz; i++)
        {
            if(next[now][s[i] - 'A'] == -1)
                next[now][s[i] - 'A'] = newnode();
            now = next[now][s[i] - 'A'];
        }
        ed[now] = 1;
    }

    bool query(char s[])
    {
        int now = root;
        for(int i = 0, sz = strlen(s); i < sz; i++)
        {
            if(next[now][s[i] - 'A'] == -1)
            {
                return false;
            }
            now = next[now][s[i] - 'A'];
        }
        return ed[now] == 1;
    }

};

4、后缀数组
/*
 * 后缀数组
 * 后缀数组的倍增构造法
 * 复杂度为O(nlogn)
 */
#define maxn (1000010)
bool cmp(int *r, int a, int b, int l)
{ return r[a] == r[b] && r[a + l] == r[b + l]; }
int ta[maxn], tb[maxn], bk[maxn];
void da(int *r, int *sa, int n, int m)
{
    int i, j, p, *x = ta, *y = tb, *t;
    for(i = 0; i < m; i++) bk[i] = 0;
    for(i = 0; i < n; i++) bk[x[i] = r[i]]++;
    for(i = 1; i < m; i++) bk[i] += bk[i-1];
    for(i = 0; i < n; i++) sa[--bk[x[i]]] = i;
    for(j = 1, p = 1; p < n; j *= 2, m = p)
    {
        for(p = 0, i = n - j; i < n; i++) y[p++] = i;
        for(i = 0; i < n; i++) if(sa[i] >= j) y[p++] = sa[i] - j;
        for(i = 0; i < m; i++) bk[i] = 0;
        for(i = 0; i < n; i++) bk[x[i]]++;
        for(i = 1; i < m; i++) bk[i] += bk[i-1];
        for(i = n-1; i >= 0; i--) sa[--bk[x[y[i]]]] = y[i];
        for(t = x, x = y, y = t, x[sa[0]] = 0, p = 1, i = 1; i < n; i++)
            x[sa[i]] = cmp(y, sa[i-1], sa[i], j)? p-1: p++;
    }
}
#define rank rrank
int rank[maxn], sa[maxn], height[maxn];
void calheight(int *r, int n)
{
    for(int i = 0; i < n; i++) rank[sa[i]] = i;
    for(int k = 0, i = 0; i < n; i++)
    {
        k ? k-- : 0;
        if(rank[i] > 0)
            while(r[i + k] == r[sa[rank[i] - 1] + k])
                k++;
        height[rank[i]] = k;
    }
}

5、Aho_Corasick自动机
/*
 * AC自动机
 * 典型问题:多模式匹配
 * 构建了fail树和Trie图,并用vis标记加速的快速解法
 */
#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
#define maxn (500050)
struct Aho_Corasick
{
    int next[maxn][26], nd[maxn], fail[maxn], vis[maxn];
    int root, L;
    int newnode()
    {
        for(int i = 0; i < 26; i++)
            next[L][i] = -1;
        nd[L] = 0;
        vis[L] = 0;
        return L++;
    }
    void init()
    {
        L = 0;
        root = newnode();
    }
    void insert(char *s)
    {
        int now = root;
        for(int i = 0, key, sz = strlen(s); i < sz; i++)
        {
            key = s[i] - 'a';
            if(next[now][key] == -1)
                next[now][key] = newnode();
            now = next[now][key];
        }
        nd[now]++;
    }
    void build()
    {
        // fail数组含义
        // 和i节点代表的前缀的后缀匹配的trie上最长真前缀,由trie树性质得唯一
        // 即当i节点的某边发生失配时转移到达的trie上最长真前缀
        // if(next[i][j] == -1) next[i][j] = next[fail[i]][j]
        queue<int> Q;
        fail[root] = root;
        for(int i = 0; i < 26; i++)
        if(next[root][i] == -1)
            next[root][i] = root;
        else
        {
            fail[next[root][i]] = root;
            Q.push(next[root][i]);
        }

        while(!Q.empty())
        {
            int now = Q.front();
            Q.pop();
            for(int i = 0; i < 26; i++)
            if(next[now][i] == -1)
                next[now][i] = next[fail[now]][i];
            else
            {
                fail[next[now][i]] = next[fail[now]][i];
                Q.push(next[now][i]);
            }
        }
    }
    int query(char *s)
    {
        int now = root, ret = 0;
        for(int i = 0, key, tmp, sz = strlen(s); i < sz; i++)
        {
            key = s[i] - 'a';
            tmp = now = next[now][key];
            while(tmp != root && !vis[tmp])
            {
                ret += nd[tmp];
                nd[tmp] = 0;
                vis[tmp] = 1;
                tmp = fail[tmp];
            }
        }
        return ret;
    }
} actree;
char keyword[100];
char text[1000010];
int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        actree.init();

        int n;
        cin >> n;
        while(n--)
        {
            scanf("%s", keyword);
            actree.insert(keyword);
        }
        actree.build();

        scanf("%s", text);
        cout << actree.query(text) << endl;
    }
    return 0;
}

三、图论


1、spfa最短路算法
/*
 * spfa最短路算法
 * 基于队列优化加速的最短路算法
 * 复杂度为O(V*E),但一般情况下很快,速度堪比dijkstra
 */
typedef long long lint;
#define maxn (100010)
int n;
vector<pair<int, int> > e[maxn];
lint dist[maxn];
queue<int> que;
bool inQue[maxn];
void spfa(lint dist[], int src)
{
    memset(dist, 63, sizeof(lint) * (n+1));
    dist[src] = 0;
    que.push(src);
    inQue[src] = true;
    while(!que.empty())
    {
        int u = que.front();
        que.pop();
        for(int i = 0, v, l; i < e[u].size(); i++)
        {
            v = e[u][i].first, l = e[u][i].second;
            if(dist[u] + l < dist[v])
            {
                dist[v] = dist[u] + l;
                if(!inQue[v])
                {
                    que.push(v);
                    inQue[v] = true;
                }
            }
        }
        inQue[u] = false;
    }
}

2、dijkstra最短路算法
/*
 * dijkstra单源最短路算法
 * +堆优化
 * 注意堆默认是大根堆,定义小根堆的时候需要将符号反向
 */
typedef long long lint;
#define maxn (100010)
const lint oo = 1e16;
vector<pair<int, int> > e[maxn << 1];
lint d1[maxn << 1], dn[maxn << 1];
bool used[maxn << 1];
struct node {
    int u; lint d;
    node(int _u, lint _d) { u = _u; d = _d; }
    bool operator< (const node& b) const
    { return d > b.d; }
};
priority_queue<node> que;
int n, m;
void dijkstra(lint d[], int src)
{
    for(int i = 1; i <= n + m; i++)
    { d[i] = oo; used[i] = false; }
    d[src] = 0;
    que.push(node(src, 0));
    while(!que.empty())
    {
        node top = que.top(); que.pop();
        int u = top.u;
        if(used[u]) continue;   /// pick out same-id node
        used[u] = true;
        for(int i = 0; i < e[u].size(); i++)
        {
            int v = e[u][i].first;
            int l = e[u][i].second;
            if(d[u] + l < d[v])
            {
                d[v] = d[u] + l;
                que.push(node(v, d[v]));    /// may out many node having same id
            }
        }
    }
}

四、数学


1、快速幂
typedef long long lint;
lint mod;
lint quick_pow(lint a, lint n)
{
    if(!n) return 1 % mod;
    lint tmp = quick_pow(a, n >> 1);
    tmp = tmp * tmp % mod;
    if(n & 1) tmp = tmp * a % mod;
    return tmp;
}

2、矩阵快速幂
typedef long long lint;
int mod;
struct matrix
{
    int a[4][4], n;
    void clear() { memset(a, 0, sizeof(a)); }
    matrix(int k, int type)
    {
        n = k, clear();
        if(type) for(int i = 0; i < n; i++)
            a[i][i] = 1;
    }
    matrix() { n = 2, clear(); a[0][0] = a[0][1] = a[1][0] = 1, a[1][1] = 0; }
    matrix operator* (const matrix& b) const
    {
        matrix o = matrix(n, 0);
        for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
        for(int k = 0; k < n; k++)
        {
            o.a[i][j] += ((lint)a[i][k] * b.a[k][j]) % mod;
            o.a[i][j] %= mod;
        }
        return o;
    }
    friend matrix operator^ (matrix tmp, int k)
    {
        matrix o = matrix(tmp.n, 1);
        while(k)
        {
            if(k & 1) o = o * tmp;
            tmp = tmp * tmp;
            k >>= 1;
        }
        return o;
    }
};
/*
 * | A E |  | A 1 |
 * | 0 E |  | 0 1 |
 */
matrix getKthsum(const matrix& b, int k)
{
    matrix tmp = matrix(b.n << 1, 0);
    for(int i = 0; i < b.n; i++)
    for(int j = 0; j < b.n; j++)
    tmp.a[i][j] = b.a[i][j];
    for(int i = 0; i < b.n; i++)
    {
        tmp.a[i][b.n + i] = 1;
        tmp.a[b.n + i][b.n + i] = 1;
    }
    tmp = tmp^(k+1);
    matrix o = matrix(b.n, 0);
    for(int i = 0; i < b.n; i++)
    for(int j = 0; j < b.n; j++)
    o.a[i][j] = tmp.a[i][b.n + j];
    for(int i = 0; i < b.n; i++)
        o.a[i][i] = (o.a[i][i] + mod - 1) % mod;

    return o;
}

3、欧几里得算法
/*
 * 辗转相除法
 * 注意a、b不可同时为0
 */
int euclid(int a, int b) { return b == 0 ? a : gcd(b, a % b); }

4、质因数分解
/*
 * 质因数分解
 * O(sqrt(n))
 * 分结束某数的所有质因数和对应次数
 */
typedef long long lint;
void factor(lint n, lint a[], int b[], int &tot)
{
    int temp = (int)((double)sqrt(n) + 1), i;
    lint now = n;
    tot = 0;
    for(i = 2; i <= temp; i++) if(now % i == 0)
    {
        a[++tot] = i;
        b[tot] = 0;
        while(now % i == 0)
        {
            b[tot]++;
            now /= i;
        }
    }
    if(now != 1)
    {
        a[++tot] = now;
        b[tot] = 1;
    }
}

4、单调队列 & 单调栈
/*
 * 单调队列
 * two-pointers
 */
int sq[maxn], st = 0, ed = 0;   //初始化
for(int i = l; i <= r; i++) //区间[l, r]入队列
{
    while(st < ed && arr[i] > sq[ed - 1]) ed--;
    sq[ed++] = arr[i];
}
for(int i = l; i <= r; i++) //区间[l, r]出队列
{
    if(arr[i] == sq[st]) st++;
}
/*
 * 单调栈
 * 用于O(n)时间内对序列中每个元素求出其左端首个小于/大于的元素
 * 常用来解决区间问题,也出现过在后缀数组题目中优化计数问题,很神的一个结构
 */
int _top;
pair<int, int> _stack[maxn];
inline void _push_stack(int x[], int w, int p)
{
    while(_top > 0)
    {
        if(w < _stack[_top].first)
            _top--;
        else
            break;
    }
    if(!_top) x[p] = -1;
    else x[p] = _stack[_top].second;
    _stack[++_top] = make_pair(w, p);
}

其他


1、读入挂
int get()
{
    int ans = 0;
    char c;
    c = getchar();
    while( !isdigit(c) ) c = getchar();
    while( isdigit(c) ) ans = ans * 10 + c - '0' , c = getchar();
    return ans;
}

2、高精度
/*
 * 高精度加法、乘法
 * MOD用10000而不是10,增加效率,节约空间
 */
struct bign
{
    #define MAX_B (100)
    #define MOD (10000)
    int a[MAX_B], n;
    bign() { a[0] = 0, n = 1; }
    bign(int num)
    {
        n = 0;
        do {
            a[n++] = num % MOD;
            num /= MOD;
        } while(num);
    }
    bign& operator= (int num)
    { return *this = bign(num); }
    bign operator+ (const bign& b) const
    {
        bign c = bign();
        int cn = max(n, b.n), d = 0;
        for(int i = 0, x, y; i < cn; i++)
        {
            x = (n > i) ? a[i] : 0;
            y = (b.n > i) ? b.a[i] : 0;
            c.a[i] = (x + y + d) % MOD;
            d = (x + y + d) / MOD;
        }
        if(d) c.a[cn++] = d;
        c.n = cn;
        return c;
    }
    bign& operator+= (const bign& b)
    {
        *this = *this + b;
        return *this;
    }
    bign operator* (const bign& b) const
    {
        bign c = bign();
        int cn = n + b.n, d = 0;
        for(int i = 0; i <= cn; i++)
            c.a[i] = 0;
        for(int i = 0; i < n; i++)
        for(int j = 0; j < b.n; j++)
        {
            c.a[i + j] += a[i] * b.a[j];
            c.a[i + j + 1] += c.a[i + j] / MOD;
            c.a[i + j] %= MOD;
        }
        while(cn > 0 && !c.a[cn-1]) cn--;
        if(!cn) cn++;
        c.n = cn;
        return c;
    }
    friend ostream& operator<< (ostream& _cout, const bign& num)
    {
        printf("%d", num.a[num.n - 1]);
        for(int i = num.n - 2; i >= 0; i--)
            printf("%04d", num.a[i]);
        return _cout;
    }
};
1 图论 3 1.1 术语 3 1.2 独立集、覆盖集、支配集之间关系 3 1.3 DFS 4 1.3.1 割顶 6 1.3.2 桥 7 1.3.3 强连通分量 7 1.4 最小点基 7 1.5 拓扑排序 7 1.6 欧拉路 8 1.7 哈密顿路(正确?) 9 1.8 Bellman-ford 9 1.9 差分约束系统(用bellman-ford解) 10 1.10 dag最短路径 10 1.11 二分图匹配 11 1.11.1 匈牙利算法 11 1.11.2 KM算法 12 1.12 网络流 15 1.12.1 最大流 15 1.12.2 上下界的网络的最大流 17 1.12.3 上下界的网络的最小流 17 1.12.4 最小费用最大流 18 1.12.5 上下界的网络的最小费用最小流 21 2 数论 21 2.1 最大公约数gcd 21 2.2 最小公倍数lcm 22 2.3 快速幂取模B^LmodP(O(logb)) 22 2.4 Fermat小定理 22 2.5 Rabin-Miller伪素数测试 22 2.6 Pollard-rho 22 2.7 扩展欧几里德算法extended-gcd 24 2.8 欧拉定理 24 2.9 线性同余方程ax≡b(mod n) 24 2.10 中国剩余定理 25 2.11 Discrete Logging(BL == N (mod P)) 26 2.12 N!最后一个不为0的数字 27 2.13 2^14以内的素数 27 3 数据结构 31 3.1 堆(最小堆) 31 3.1.1 删除最小值元素: 31 3.1.2 插入元素和向上调整: 32 3.1.3 堆的建立 32 3.2 并查集 32 3.3 树状数组 33 3.3.1 LOWBIT 33 3.3.2 修改a[p] 33 3.3.3 前缀和A[1]+…+A[p] 34 3.3.4 一个二维树状数组的程序 34 3.4 线段树 35 3.5 字符串 38 3.5.1 字符串哈希 38 3.5.2 KMP算法 40 4 计算几何 41 4.1 直线交点 41 4.2 判断线段相交 41 4.3 三点外接圆圆心 42 4.4 判断点在多边形内 43 4.5 两圆交面积 43 4.6 最小包围圆 44 4.7 经纬度坐标 46 4.8 凸包 46 5 Problem 48 5.1 RMQ-LCA 48 5.1.1 Range Minimum Query(RMQ) 49 5.1.2 Lowest Common Ancestor (LCA) 53 5.1.3 Reduction from LCA to RMQ 56 5.1.4 From RMQ to LCA 57 5.1.5 An<O(N), O(1)> algorithm for the restricted RMQ 60 5.1.6 An AC programme 61 5.2 最长公共子序列LCS 64 5.3 最长上升子序列/最长不下降子序列(LIS) 65 5.3.1 O(n^2) 65 5.3.2 O(nlogn) 66 5.4 Joseph问题 67 5.5 0/1背包问题 68 6 组合数学相关 69 6.1 The Number of the Same BST 69 6.2 排列生成 71 6.3 逆序 72 6.3.1 归并排序求逆序 72 7 数值分析 72 7.1 二分法 72 7.2 迭代法(x=f(x)) 73 7.3 牛顿迭代 74 7.4 数值积分 74 7.5 高斯消元 75 8 其它 77
ACM 算法模板集 Contents 一. 常用函数与STL 二. 重要公式与定理 1. Fibonacci Number 2. Lucas Number 3. Catalan Number 4. Stirling Number(Second Kind) 5. Bell Number 6. Stirling's Approximation 7. Sum of Reciprocal Approximation 8. Young Tableau 9. 整数划分 10. 错排公式 11. 三角形内切圆半径公式 12. 三角形外接圆半径公式 13. 圆內接四边形面积公式 14. 基础数论公式 三. 大数模板,字符读入 四. 数论算法 1. Greatest Common Divisor最大公约数 2. Prime素数判断 3. Sieve Prime素数筛法 4. Module Inverse模逆元 5. Extended Euclid扩展欧几里德算法 6. Modular Linear Equation模线性方程(同余方程) 7. Chinese Remainder Theorem中国余数定理(互素于非互素) 8. Euler Function欧拉函数 9. Farey总数 9. Farey序列构造 10. Miller_Rabbin素数测试,Pollard_rho因式分解 五. 图论算法 1. 最小生成树(Kruscal算法) 2. 最小生成树(Prim算法) 3. 单源最短路径(Bellman-ford算法) 4. 单源最短路径(Dijkstra算法) 5. 全源最短路径(Folyd算法) 6. 拓扑排序 7. 网络预流和最大流 8. 网络最小费用最大流 9. 网络最大流(高度标号预流推进) 10. 最大团 11. 二分图最大匹配(匈牙利算法) 12. 带权二分图最优匹配(KM算法) 13. 强连通分量(Kosaraju算法) 14. 强连通分量(Gabow算法) 15. 无向图割边割点和双连通分量 16. 最小树形图O(N^3) 17. 最小树形图O(VE) 六. 几何算法 1. 几何模板 2. 球面上两点最短距离 3. 三点求圆心坐标 4. 三角形几个重要的点 七. 专题讨论 1. 树状数组 2. 字典树 3. 后缀树 4. 线段树 5. 并查集 6. 二叉堆 7. 逆序数(归并排序) 8. 树状DP 9. 欧拉路 10. 八数码 11. 高斯消元法 12. 字符串匹配(KMP算法) 13. 全排列,全组合 14. 二维线段树 15. 稳定婚姻匹配 16. 后缀数组 17. 左偏树 18. 标准RMQ-ST 19. 度限制最小生成树 20. 最优比率生成树(0/1分数规划) 21. 最小花费置换 22. 区间K大数 23. LCA - RMQ-ST 24. LCA – Tarjan 25. 指数型母函数 26. 指数型母函数(大数据) 27. 单词前缀树(字典树+KMP) 28. FFT(大数乘法) 29. 二分图网络最大流最小割 30. 混合图欧拉回路 31. 无源汇上下界网络流 32. 二分图最小点权覆盖 33. 带约束的轨道计数(Burnside引理) 34. 三分法求函数波峰 35. 单词计数,矩阵乘法 36. 字符串和数值hash 37. 滚动队列,前向星表示法 38. 最小点基,最小权点基
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值