My Standard Code Library

=============图论=============

匈牙利算法

// 时间复杂度为 O(n4) 的匈牙利算法,可继续优化到 O(n3) 
// 等我来优化之

int W[maxn][maxn], n;
int Lx[maxn], Ly[maxn];     // 顶标
int left[maxn];             // left[i]为右边第 i 个点的编号
bool S[maxn], T[maxn];      // S[i] 和 T[i] 为左/右第 i 个点是否已经标记

bool match(int i) {
    S[i] = true;
    for (int j = 1; j <= n; j++) if (Lx[i] + Ly[i] == W[i][j] && !T[j]) {
        T[j] = true;
        if (!left[j] || match(left[j])) {
            left[j] = i;
            return true;
        }
    }
    return false;
}

void update() {
    int a = 1 << 30;
    for (int i = 1; i <= n; i++) if (S[i]) {
        for (int j = 1; j <= n; j++) if (!T[j]) {
            a = min(a, Lx[i] + Ly[j] - W[i][j]);
        }
    }
    for (int i = 1; i <= n; i++) {
        if (S[i]) Lx[i] -= a;
        if (T[i]) Ly[i] += a;
    }
}

void KM() {
    for (int i = 1; i <= n; i++) {
        left[i] = Lx[i] = Ly[i] = 0;
        for (int j = 1; j <= n; j++)
            Lx[i] = max(Lx[i], W[i][j]);
    }
    for (int i = 1; i <= n; i++) {
        for (;;) {
            for (int j = 1; j <= n; j++) S[j] = T[j] = 0;
            if (match(i)) break;
            else update();
        }
    }   
}

最小割最大流

// EdmondsKarp
// 需要包含的库
#include <vector>
#include <queue>
#include <algorithm>

using namespace std;

// 需要定义的量
const int INF = 100000000;
const int maxn = 1005;

struct Edge {
    int from, to, cap, flow;
    Edge(int _from, int _to, int _cap, int _flow)
        : from(_from), to(_to), cap(_cap), flow(_flow) {}
};

struct EdmondsKarp {
    int n, m;
    vector<Edge> edges; // 边数的两倍
    vector<int> G[maxn];    // 邻接表,G[i][j]表示节点 i 的第 j 条变在 e 数组中的序号
    int a[maxn];            // 起点到 i 的可改进量
    int p[maxn];            // 最短路上 p 的入弧编号

    void init(int n) {
        for (int i = 0; i < n; ++i) G[i].clear();
        edges.clear();
    }
    void AddEdge(int from, int to, int cap) {
        edges.push_back(Edge(from, to, cap, 0));
        edges.push_back(Edge(to, from, 0, 0));      // 反向弧
        m = edges.size();
        G[from].push_back(m - 2);
        G[to].push_back(m - 1);
    }

    int Maxflow(int s, int t) {
        int flow = 0;
        for (;;) {
            memset(a, 0, sizeof(a));
            queue<int> Q; Q.push(s);
            a[s] = INF;
            while (!Q.empty()) {
                int x = Q.front(); Q.pop();
                for (int i = 0; i < G[x].size(); ++i) {
                    Edge &e = edges[G[x][i]];
                    if (!a[e.to] && e.cap > e.flow) {
                        p[e.to] = G[x][i];
                        a[e.to] = min(a[x], e.cap - e.flow);
                        Q.push(e.to);
                    }
                }
                if (a[t]) break;
            }
            if (!a[t]) break;
            for (int u = t; u != s; u = edges[p[u]].from) {
                edges[p[u]].flow += a[t];
                edges[p[u] ^ 1].flow -= a[t];
            }
            flow += a[t];
        }
        return flow;
    }
};
// Dinic
// 最近这些算法都很少有注释,原因是我还没真正弄清它们的本质
// 所以暂且不加入注释和讲解,以免丢人现眼
struct Dinic {
    int n, m, s, t;
    vector<Edge> edges;
    vector<int> G[maxn];
    bool vis[maxn];
    int d[maxn];
    int cur[maxn];

    bool BFS() {
        memset(vis, 0, sizeof(vis));
        queue<int> Q;
        Q.push(s);
        d[s] = 0;
        vis[s] = 1;
        while (!Q.empty()) {
            int x = Q.front(); Q.pop();
            for (int i = 0; i < G[x].size(); i+) {
                Edge &e = edges[G[x][i]];
                if (!vis[e.to] && e.cap > e.flow) {
                    vis[e.to] = 1;
                    d[e.to] = d[x] + 1;
                    Q.push(e.to);
                }
            }
        }
        return vis[t];
    }

    int DFS(int x, int a) {
        if (x == t || a == 0) return a;
        int flow = 0, f;
        for (int &i = cur[x]; i < G[x].size(); i++) {
            Edge &e = edges[G[x][i]];
            if (d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap - e.flow))) > 0) {
                e.flow += f;
                edges[G[x][i] ^ 1].flow -= f;
                flow += f;
                a -= f;
                if (a == 0) break;
            }
        }
        return flow;

    }
    int maxflow(int s, int t) {
        this->s = s; this->t = t;
        int flow = 0;
        while (BFS()) {
            memset(cur, 0, sizeof(cur));
            flow += DFS(s, INF);
        }
        return flow;
    }
};

最小费用最大流 MCMF

// 需要包含的库
#include <vector>
#include <queue>

using namespace std;

// 需要定义的量,此处暂且用这些数值代替,按照实际需求编写
const int maxn = 1005;
const int INF = 100000000;


struct Edge {
    int from, to, cap, flow, cost;
    Edge(int _from, int _to, int _cap, int _flow, int _cost)
        : from(_from), to(_to), cap(_cap), flow(_flow), cost(_cost) {}
};

struct MCMF {
    int n, m, s, t;
    vector<Edge> edges;
    vector<int> G[maxn];
    int inq[maxn];
    int d[maxn];
    int p[maxn];
    int a[maxn];

    void init(int n) {
        this->n = n;
        for (int i = 0; i < n; ++i) G[i].clear();
        edges.clear();
    }

    void AddEdge(int from, int to, int cap, int cost) {
        edges.push_back(Edge(from, to, cap, 0, cost));
        edges.push_back(Edge(to, from, 0, 0, -cost));
        m = edges.size();
        G[from].push_back(m - 2);
        G[to].push_back(m - 1);
    }

    bool BellmanFord(int s, int t, int &flow, long long &cost) {
        for (int i = 0; i < n; ++i) d[i] = INF;
        memset(inq, 0, sizeof(inq));
        d[s] = 0; inq[s] = 1; p[s] = 0; a[s] = INF;

        queue<int> Q;
        while (!Q.empty()) {
            int u = Q.front(); Q.pop();
            inq[u] = 0;
            for (int i = 0; i < G[u].size(); ++i) {
                Edge &e = edges[G[u][i]];
                if (e.cap > e.flow && d[e.to] > d[u] + e.cost) {
                    d[e.to] = d[u] + e.cost;
                    p[e.to] = G[u][i];
                    a[e.to] = min(a[u], e.cap - e.flow);
                    if (!inq[e.to]) { Q.push(e.to); inq[e.to] = 1; }
                }
            }
        }
        if (d[t] == INF) return false;
        flow += a[t];
        cost += (long long)d[t] * (long long)a[t];
        for (int u = t; u != s; u = edges[p[u]].from) {
            edges[p[u]].flow += a[t];
            edges[p[u] ^ 1].flow -= a[t];
        }
        return true;
    }

    int MincostMaxflow(int s, int t, long long &cost) {
        int flow = 0; cost = 0;
        while (BellmanFord(s, t, flow, cost));
        return flow;
    }
};

Dijkstra

struct Edge {
    int from, to, dist;
    Edge(int u, int v, int d) : from(u), to(v), dist(d) {}
};

struct HeapNode {
    int d, u;
    bool operator < (const HeapNode &rhs) const {
        return d > rhs.d;
    }
    HeapNode(int dd = 0, int uu = 0) : d(dd), u(uu) {}
};

struct Dijkstra {
    int n, m;
    vector<Edge> edges;
    vector<int> G[maxn];
    bool done[maxn];
    int d[maxn];
    int p[maxn];

    void init(int n) {
        this->n = n;
        for (int i = 0; i < n; ++i) G[i].clear();
        edges.clear();
    }

    void AddEdge(int from, int to, int dist) {
        edges.push_back(Edge(from, to, dist));
        m = edges.size();
        G[from].push_back(m - 1);
    }

    void dijkstra(int s) {
        priority_queue<HeapNode> Q;
        for (int i = 0; i < n; ++i) d[i] = INF;
        d[s] = 0;
        memset(done, 0, sizeof(done));
        Q.push(HeapNode(0, s));
        while (!Q.empty()) {
            HeapNode x = Q.top(); Q.pop();
            int u = x.u;
            if (done[u]) continue;
            for (int i = 0; i < G[u].size(); ++i) {
                Edge &e = edges[G[u][i]];
                if (d[e.to] > d[u] + e.dist) {
                    d[e.to] = d[u] + e.dist;
                    p[e.to] = G[u][i];
                    Q.push(HeapNode(d[e.to], e.to));
                }
            }
        }
    }
};

Bellman-Ford的SPFA改进算法
在一些代码中没有直接使用Bellman-Ford算法,主要原因是效率问题。于是出现这个SPFA改进的算法,维护一个队列存储点

bool bellman_ford(int s) 
{
    queue<int> Q;
    memset(inq, 0, sizeof(inq));
    memset(cnt, 0, sizeof(cnt));
    for (int i = 0; i < n; ++i) d[i] = INF;
    d[s] = 0;
    inq[s] = true;
    Q.push(s);

    while (!Q.empty()) {
        int u = Q.front(); Q.pop();
        inq[u] = false;
        for (int i = 0; i < G[u].size(); ++i) {
            Edge &e = edges[G[u][i]];
            if (d[u] < INF && d[e.to] > d[u] + e.dist) {
                d[e.to] = d[u] + e.si;
                p[e.to] = G[u][i];
                if (!inq[e.to]) {
                    Q.push(e.to);
                    inq[e.to] = true;
                    if (++cnt[e.to] > n)
                        return false;
                }
            }
        }
    }
    return true;
}
// 下列为一成品SPFA的封装
struct SPFA {
    int n, m;
    vector<Edge> edges;
    vector<int> G[N];
    bool vis[N];
    long long d[N];
    int p[N];

    void init(int _n) {
        n = _n;
    }

    void relief() {
        for (int i = 0; i < n; ++i)
            G[i].clear();
        edges.clear();
    }

    void addedge(int from, int to, int spst) {
        edges.push_back(Edge(from, to, spst));
        m = edges.size();
        G[from].push_back(m - 1);
    }

    void spfa(int s) {
        queue<int> Q;
        // 清空队列,所有元素均不再队列中
        while (!Q.empty())  Q.pop();
        // vis[] ~~ inq[]
        for (int i = 0; i < n; ++i) {
            d[i] = INF;
            vis[i] = 0;
        }
        d[s] = 0;
        vis[s] = 1;
        Q.push(s);

        while (!Q.empty()) {
            int u = Q.front();  Q.pop();
            vis[u] = 0;
            for (int i = 0; i < G[u].size(); ++i) {
                Edge &e = edges[G[u][i]];
                if (d[e.to] > d[u] + e.spst) {
                    d[e.to] = d[u] + e.spst;
                    p[e.to] = G[u][i];
                    if (!vis[e.to]) {
                        vis[e.to] = 1;
                        Q.push(e.to);
                    }
                }
            }
        }
    }
};

Kruskal
TODO:将算法中的并查集改成这个模板中的并查集算法

// 把所有边排序,记第 i 小的边为 e[i] (1 <= i < m)
// 初始化 MST 为空
// 初始化连通分量,让每一个点自成一个独立的连通分量
// for (int i = 0; i < m; ++i)
//     if (e[i].u 与 e[i].v 不在同一个连通分量) {
//         把边 e[i] 加入 MST
//         合并 e[i].u 和 e[i].v 所在的连通分量
// 上述伪代码中关键在于“连通分量的查询与合并”:需要知道两个点是否在一个连通分量,
// 还需要合并连通分量。

// 如下为使用并查集的 Kruskal 算法
// 第 i 条边的两个端点号和权值存在 u[i], v[i], w[i] 中
int cmp(const int i, const int j) { return w[i] < w[j]; }       // 间接排序函数
int find(int x) { return p[x] == x ? x : p[x] = find(p[x]); }   // 并查集查找
// 排序后第 i 小的边的序号存在 r[i] 中
int Kruskal()
{
    int ans = 0;
    for (int i = 0; i < n; ++i) p[i] = i;       // 初始化并查集
    for (int i = 0; i < m; ++i) r[i] = i;       // 初始化边序号
    sort(r, r + m, cmp);
    for (int i = 0; i < m; ++i) { 
        int e = r[i];
        int x = find(u[e]);
        int y = find(v[e]);
        if (x != y) {
            ans += w[e];
            p[x] = y;
        }
    }
    return ans;
}

============数据结构============

无根树转有根树

vector<int> G[maxn];
// read_tree用来读取一个图,此图可以看作是一个无根树
void read_tree()
{
    int u, v;
    scanf("%d", &n);
    for (int i = 0; i < n - 1; ++i) {
        scanf("%d%d", &u, &v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
}
// dfs用于转化成有根树
void dfs(int u, int fa)
{
    int d = G[u].size();
    for (int i = 0; i < d; ++i) {
        int v = G[u][i];
        if (v != fa)
            dfs(v, p[v] = u);
    }
}

树状数组
TODU:完善树状数组的代码及使用方式的注释


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

int sum(int x)
{
    int ret = 0;
    while (x > 0) {
        ret += C[x];
        x -= lowbit(x);
    }
    return ret;
}

void add(int x, int d)
{
    while (x <= n) {
        C[x] += d;
        x += lowbit(x);
    }
}

并查集

// 并查集是一种用来管理元素分组情况的数据结构。并查集可以搞笑地进行如下操作。
// 不过需要注意并查集虽然可以进行合并操作,但是无法进行分割操作。
// *查询元素a和元素b是否属于同一组。
// *合并元素a和元素b所在的组。

// 并查集也是使用树形结构实现的。不过不是二叉树。每个元素对应一个节点,每个组
// 对应一个树。在并查集中,哪个节点是哪个节点的父亲以及树的形状等信息无需多加
// 关注,整体组成一个树形结构才是重要的。

// (1)初始化:  准备n个节点来表示n个元素,最开始没有边。
// (2)合并:       从一个组的根向另一个组的根连边,这样两棵树就变成了一棵树,也就
//                  把两个组合并为一个组了。
// (3)查询:       为了查询两个节点是否属于同一组,需要沿着树向上走,来查询包含这
//                  个元素的书的根是谁,如果两个节点有同一根,则属于同一组。

// ======================================================

int par[MAX_N];             // 父亲
int rank[MAX_N];                // 树的高度

// 初始化n个元素
void init(int n) 
{
    for (int i = 0; i < n; i++) 
    {
        par[i] = i;
        rank[i] = 0;
    }
}

// 查询树的根
int find(int x)
{
    if (par[x] == x)
        return x;
    else
        return par[x] = find(par[x]);
}

// 合并x和y所属的集合
void unite(int x, int y)
{
    x = find(x);
    y = find(y);
    if (x == y) return;

    if (rank[x] < rank[y]) {
        par[x] = y;
    }
    else {
        par[y] = x;
        if (rank[x] == rank[y])
            rank[x]++;
    }
}

// 判断x和y是否属于同一个集合
bool same(int x, int y)
{
    return find(x) == find(y);
}

=============数论=============

素数筛选

/* ---------------------- Prime SCL ---------------------- */
// 运行后prime数组的prime[i]即为正整数i是否为素数的标识,true表示是素数
int prime[MAXN];
void getPrime() 
{
    memset(prime, true, sizeof(prime));
    prime[0] = prime[1] = false;
    for (int i = 2; i < MAXN; ++i)  if (prime[i]) {
        if (i > MAXN / i)   continue;
        for (int j = i * i; j < MAXN; j += i)
            prime[j] = false;
    }
}
/* ---------------------- Prime SCL ---------------------- */

GCD 和 LCM

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

int lcm(int a, int b)
{
    return a * b / gcd(a, b);
}

ExGCD

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

取余模算术
a * b % n 的规模相当大,那么对 a 先取模再对 b 取模然后再进行乘法再取模

int mul_mod(int a, int b, int n)
{
    a %= n;
    b %= n;
    return (int)((long long) a * b % n);
}

大整数的取余
n 为大整数的char 数组,传入 n 和 m 返回这个余数,m 是 int 类型,所以返回也是 int 类型

int large_mod(char *n, int m)
{
    int len = strlen(n);
    int ans = 0;
    for (int i = 0; i <len; ++i)
        ans= (int)(((long long) ans * 10 + n[i] - '0') % m);
    return ans;
}

幂取模
输入正整数 a、n 和 m,输出 a ^ n mod m 的值。a,n,m <= 10 ^ 9

int pow_mod(int a, int n, int m)
{
    if (n == 0) return 1;
    int x = pow_mod(a, n / 2, m);
    long long ans = (long long) x * x % m;
    if (n % 2 == 1) ans = ans * a % m;
    return (int) ans;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值