首页 > 其他分享 >蒟蒻的模板

蒟蒻的模板

时间:2024-02-07 10:22:05浏览次数:25  
标签:std int dfn maxn 模板 now es

蒟蒻 Rainbow_Automaton 的模板

\(\text{2023-10}\) 备战 \(\text{csp-s}\)

只是目前会的

然而目前啥也不会...

代码注意事项

  • 不要使用using namespace std;

  • minmax 都可以直接 std::min std::max

  • 关同步 #define fastread std::ios_sync_with_stdio (false); cin.tie (0);

  • 学习 jiangly using i64 = long long

图论

存图

这种东西真的需要记吗

写一个神秘的封装好的图

template<int maxn, int maxm>
struct Graph {
    struct Edge {
        int u, v;
        int pre;
    } es[maxm << 1];

    int last[maxn], cnt;

    Graph () { cnt = 0; memset (last, 0, sizeof (last)); }

    inline void addEdge (int u, int v) {
        es[++cnt] = Edge { u, v, last[u] };
        last[u] = cnt;
    }
};

然而我大部分时侯都不会用到封装好的图...

一般情况下只会直接写 Graph 里面的东西

最短路

Dijkstra

struct Node {
    int id;
    int dis;

    friend bool operator < (Node a, Node b) {
        return a.dis > b.dis; // 通过改符号的方向实现小根堆...
    }
};

std::priority_queue<Node> q;

int dis[maxn];
bool vis[maxn];
inline void dij (int S) {
    memset (vis, 0, sizeof (vis));
    memset (dis, 0x3f, sizeof (dis)); dis[S] = 0;
    q.push (Node { S, dis[S] });

    while (not q.empty()) {
        int now = q.top ().id; q.pop ();

        if (vis[now]) { continue; }
        vis[now] = true;

        for (int i = last[now]; i; i = es[i].pre) {
            int t = es[i].v;

            if (dis[t] > dis[now] + es[i].w) {
                dis[t] = dis[now] + es[i].w;
                q.push (Node { t, dis[t] });
            }
        }
    }
}

某个广为人所知的最短路算法

判负环

不判负环最好别用

才发现我单源最短路都是用 Dijkstra 过的

甚至找不到一个SPFA的板子

std::queue<int> q;
bool inq[maxn];
int times[maxn];

int dis[maxn];
inline bool spfa (int S) {
    q.push (S);
    memset (inq, 0, sizeof (inq)); inq[S] = true;
    memset (times, 0, sizeof (times)); times[S] = 0;
    for (int i = 1; i <= n + 1; i++) { if (i != S) { dis[i] = inf; } }

    while (not q.empty ()) {
        int now = q.front (); q.pop ();
        inq[now] = false;

        for (int i = last[now]; i; i = es[i].pre) {
            int t = es[i].v;

            if (dis[t] > dis[now] + es[i].w) {
                dis[t] = dis[now] + es[i].w;

                if (not inq[t]) {
                    inq[t] = true;
                    times[t] ++; if (times[t] > n + 1) { return false; } // 此处n + 1是因为多了一个超级源

                    q.push (t);
                }
            }
        }
    }

    return true;
}
差分约束

也许是唯一能用上SPFA的地方吧...

可以解出 \(m\) 个形如 \(X_u - X_v \leq Y\) 的不等式组成的不等式组的一个解

自然也可以判断是否有解

实质就是 $u - v \leq w \implies u \leq v + w \implies Edge_{v, u, w} $

\(v\) 到 \(u\) 连一条长度为 \(w\) 的边

inline void diff_constraints () { // 差分约束
    cin >> n >> m;

    for (int i = 1; i <= m; i++) {
        int u, v, w; cin >> u >> v >> w;

        addEdge (v, u, w);
    }

    // 以n + 1作为超级源点
    for (int i = 1; i <= n; i++) { addEdge (n + 1, i, 0); }

    if (not spfa (n + 1)) { cout << "NO" << endl; }
    else {
        for (int i = 1; i <= n; i++) { cout << dis[i] << " "; }
    }
}   

Floyd

int dis[maxn][maxn];

inline void init () {
    memset (dis, 0x3f, sizeof (dis));
    for (int i = 1; i <= n; i++) { dis[i][i] = 0; }
}

inline void floyd () {
    for (int k = 1; k <= n; k++) {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                dis[i][j] = std::min (dis[i][j], dis[i][k] + dis[k][j]);
            }
        }
    }
}

注意先 init() 再加边

倍增Floyd

实际上是矩阵快速幂

把 \(\text{Floyd}\) 原来的用 \(dp[k][i][j]\) 表示前 \(k\) 个点中 \(i\) 到 \(j\) 的最短路

改为用 \(dp[s][i][j]\) 表示长度为 \(s\) 的 \(i\) 到 \(j\) 的最短路

显然

\(dp[s][i][j]\) 可以由 $ \min\limits_{k = 1}^{n} {dp[s - 1][i][k] + dp[1][k][j]}$ 转移过来

然后我们发现转移的过程和矩阵乘法特别像

用快速幂求出 \(dp^k\) 就可以得到长为 \(k\) 的路径条数

传递闭包

实际上就是 \(01\) 矩阵上的 \(\text{Floyd}\)

只是求最短路改成求连通性了

std::bitset<maxn> a[maxn];

inline void floydClosure () {
    for (int j = 1; j <= n; j++) {
        for (int i = 1; i <= n; i++) {
            if (a[i][j]) { // i 能到 j
                a[i] |= a[j]; // 用j更新i
            }
        }
    }
}

上面的代码是用 bitset 写的, 与传统 \(\text{Floyd}\) 略有不同

但是其基本思想是类似的

生成树

Kruskal

struct Edge {
    int u, v, w;

    friend bool operator < (Edge a, Edge b) {
        return a.w < b.w;
    }
};
std::vector<Edge> es;

inline int kruskal () {
    std::sort (es.begin (), es.end ());
    init ();

    int ans = 0;
    int tot = 0;

    for (auto e : es) {
        int u = find (e.u);
        int v = find (e.v);

        if (u == v) { continue; }

        fa[u] = v;
        ans += e.w;

        tot ++;
        if (tot == n - 1) { return ans; }
    }

    return -1;
}

其中 init() 就是并查集的 init()

并查集的写法看数据结构部分吧

Tarjan 系列

强连通分量

int low[maxn], dfn[maxn], dpos;
int stk[maxn], spos;
bool ins[maxn];

int belong[maxn], scc_cnt;
std::set<int> scc[maxn];

void tarjan (int now) {
    low[now] = dfn[now] = ++dpos;
    stk[++spos] = now;
    ins[now] = true;

    for (int i = last[now]; i; i = es[i].pre) {
        int t = es[i].v;

        if (not dfn[t]) {
            tarjan (t);
            low[now] = std::min (low[now], low[t]);
        } else if (ins[t]) {
            low[now] = std::min (low[now], dfn[t]);
        }
    }

    if (low[now] == dfn[now]) {
        scc_cnt ++;
        do {
            belong[now] = scc_cnt;
            scc[scc_cnt].insert (now);
            ins[now] = false;

            now = stk[spos--];
        } while (low[now] != dfn[now]);
    }
}
2-SAT

用 \(1\) 到 \(n\) 表示 \(a_i\) 取 \(0\)

用 \(n + 1\) 到 \(2 \times n\) 表示 \(a_{i - n}\) 取 \(1\)

for (int i = 1; i <= 2 * n; i++) {
    if (not dfn[i]) { tarjan (i); }
}

if (not check ()) { std::cout << "IMPOSSIBLE" << "\n"; return 0; }

std::cout << "POSSIBLE" << "\n";
for (int i = 1; i <= n; i++) {
    if (scc[i + n] < scc[i]) { std::cout << 1 << " "; }
    else { std::cout << 0 << " "; }
}

割点

int low[maxn], dfn[maxn], dpos;
bool cut[maxn];

void tarjan (int now, int fa) {
    low[now] = dfn[now] = ++dpos;

    int sons = 0;
    for (int i = last[now]; i; i = es[i].pre) {
        int t = es[i].v;

        if (not dfn[t]) {
            tarjan (t, now);
            low[now] = std::min (low[now], low[t]);
            sons ++;
            if (low[t] >= dfn[now]) { cut[now] = true; }
        } else { // 此处可以考虑父亲
            low[now] = std::min (low[now], dfn[t]);
        }            
    }

    if (fa == 0 and sons < 2) { cut[now] = false; } 
} 
点双连通分量
int low[maxn], dfn[maxn], dpos;
int stk[maxn], spos;

int belong[maxn], vdcc_cnt;
std::vector<int> vdcc[maxn];

void tarjan (int now, int fa) {
    low[now] = dfn[now] = ++dpos;
    stk[++spos] = now;

    int sons = 0;
    for (int i = last[now]; i; i = es[i].pre) {
        int t = es[i].v;

        if (not dfn[t]) {
            tarjan (t, now);
            low[now] = std::min (low[now], low[t]);

            sons ++;

            if (low[t] >= dfn[now]) {
                ++vdcc_cnt;

                int last = 0;
                while (last != t) {
                    int top = stk[spos--];
                    belong[top] = vdcc_cnt;
                    vdcc[vdcc_cnt].push_back (top);         
                    last = top;
                }
                vdcc[vdcc_cnt].push_back (now);
            }    
        } else {
            low[now] = std::min (low[now], dfn[t]);
        }
    }

    if (fa == 0 and sons == 0) { vdcc_cnt ++; vdcc[vdcc_cnt].push_back (now); }
}

int low[maxn], dfn[maxn], dpos;
bool bridge[maxm << 1];

void tarjan (int now, int in_edge) {
    low[now] = dfn[now] = ++dpos;

    for (int i = last[now]; i; i = es[i].pre) {
        int t = es[i].v;

        if (not dfn[t]) {
            tarjan (t, i);
            low[now] = std::min (low[now], low[t]);

            if (low[t] > dfn[now]) { bridge[i] = bridge[i ^ 1] = true; }

        } else if (i != (in_edge ^ 1)) {
            low[now] = std::min (low[now], dfn[t]);
        }
    }
}

inline void init () { cnt = 1; }

因为涉及到快速找反向边, 一定要把 cnt 初始化为1!

边双连通分量
int low[maxn], dfn[maxn], dpos;
bool bridge[maxm << 1];

void tarjan (int now, int in_edge) {
    low[now] = dfn[now] = ++dpos;

    for (int i = last[now]; i; i = es[i].pre) {
        int t = es[i].v;

        if (not dfn[t]) {
            tarjan (t, i);
            low[now] = std::min (low[now], low[t]);

            if (low[t] > dfn[now]) { bridge[i] = bridge[i ^ 1] = true; }

        } else if (i != (in_edge ^ 1)) {
            low[now] = std::min (low[now], dfn[t]);
        }
    }
}   

int belong[maxn], edcc_cnt;
std::vector<int> edcc[maxn];

void dfs (int now) {
    belong[now] = edcc_cnt;
    edcc[edcc_cnt].push_back (now);
    for (int i = last[now]; i; i = es[i].pre) {
        int t = es[i].v;
        if (bridge[i]) { continue; }
        if (belong[t]) { continue; }

        dfs (t);
    }
}

inline void init () { cnt = 1; }

网络流

网络流的加边方式与普通图论问题的加边方式略有不同

struct Edge {
   int u, v;
   int pre;
   long long flow;
} es[maxm << 1];

int last[maxn], cur[maxn], cnt = 1;
int S, T;

inline void _addEdge (int u, int v, long long cap) {
   es[++cnt] = Edge { u, v, last[u], cap };
   last[u] = cnt;
}

inline void addEdge (int u, int v, long long cap) {
   _addEdge (u, v, cap);
   _addEdge (v, u, 0);
}

Dinic

int dep[maxn];

bool bfs () {
    std::queue<int> q; q.push (S);
    memset (dep, 0x3f, sizeof (dep)); dep[S] = 1;

    while (not q.empty ()) {
        int now = q.front (); q.pop ();

        for (int i = last[now]; i; i = es[i].pre) {
            int t = es[i].v;
            if (not es[i].flow) { continue; }
            if (dep[t] != 0x3f3f3f3f) { continue; }

            dep[t] = dep[now] + 1;
            q.push (t);
        }
    }

    return dep[T] != 0x3f3f3f3f;
}

long long dfs (int now, long long now_flow) {
    if (now == T or not now_flow) { return now_flow; }

    long long res = 0;
    for (int &i = cur[now]; i; i = es[i].pre) {
        int t = es[i].v;

        if (not es[i].flow) { continue; }
        if (dep[t] != dep[now] + 1) { continue; }

        long long t_flow = dfs (t, std::min (now_flow, es[i].flow));

        if (t_flow) {
            es[i].flow -= t_flow;
            es[i ^ 1].flow += t_flow;
            now_flow -= t_flow;
            res += t_flow;

            if (not now_flow) { return res; }
        }
    }

    return res;
}

inline long long Dinic (int _s, int _t) {
    S = _s, T = _t;
    long long res = 0;
    while (bfs ()) {
        memcpy (cur, last, sizeof (last));
        res += dfs (S, 0x3f3f3f3f);
    }
    return res;
}

网络单纯形

咕咕咕

呜呜呜本来是会的

字符串

啥也不会

后缀自动机

struct SAM {
    struct Node {
        int ch[26];
        int fa;
        int len;
    } ns[maxn];

    int last, root, tot;

    // 附加信息
    i64 cnt[maxn];

    SAM () { last = root = tot = 1; memset (cnt, 0, sizeof (cnt)); }

    inline void add (int c) {
        int p = last;
        int np = last = ++tot;

        ns[np].len = ns[p].len + 1;
        cnt[np] = 1;

        for (; p and not ns[p].ch[c]; p = ns[p].fa) { ns[p].ch[c] = np; }

        if (not p) { ns[np].fa = root; }
        else {
            int q = ns[p].ch[c];

            if (ns[q].len == ns[p].len + 1) { ns[np].fa = q; }
            else {
                int nq = ++tot;
                ns[nq] = ns[q]; ns[nq].len = ns[p].len + 1;
                ns[q].fa = ns[np].fa = nq;

                for (; p and ns[p].ch[c] == q; p = ns[p].fa) { ns[p].ch[c] = nq; }
            }
        }
    }    

    inline void getParentTree (Graph& tr) {
        for (int i = 2; i <= tot; i++) { tr.addEdge (ns[i].fa, i); }
    }
};

数学

快速幂

i64 ksm (i64 a, i64 b, i64 mod) {
    i64 res = 1;
    i64 base = a;

    while (b) {
        if (b & 1) { (res *= base) %= mod; }
        (base *= base) %= mod;
        b >>= 1;
    }    

    return res;
}

欧拉函数

i64 phi (i64 n) {
    i64 ans = n;
    for (i64 p = 2; p * p <= n; p++) {
        if (n % p != 0) { continue; }

        ans = ans / p * (p - 1);
        while (n % p == 0) { n /= p; }
    }

    if (n > 1) { ans = ans / n * (n - 1); }

    return ans;
}

逆元

这里使用欧拉函数求逆元

不会写exgcd导致的

你说的对, 但是 \(a ^ {\varphi(b)} = 1 \pmod b\)

所以我们可以直接求逆元

i64 inv (i64 a, i64 b) {
    return Ksm::ksm (a, phi (b) - 1, b);
}

exgcd

i64 exgcd (i64 a, i64 b, i64 &x, i64 &y) {
    if (b == 0) { x = 1, y = 0; return a; }
    i64 d = exgcd (b, a % b, y, x);
    y -= x * (a / b);
    return d;
}

于是, 上面的逆元也可以用exgcd求

因为

\[a \times inv_a \equiv 1 \pmod b \\ a \times inv_a + b \times y = 0 \]

所以我们很容易用exgcd求出 \(inv_a\)

inline i64 inv (i64 a, i64 b) {
    i64 x, y; exgcd (a, b, x, y);
    return (x + b) % b;       
}

线性筛

std::bitset<maxn> not_prime;
std::vector<int> primes;

inline void get_primes () {
    not_prime.reset ();
    not_prime[1] = true;

    for (int i = 2; i <= n; i++) {
        if (not not_prime[i]) { primes.push_back (i); }

        for (auto p : primes) {
            if (i * p > n) { break; }
            not_prime[i * p] = true;
            if (i % p == 0) { break; }
        }
    }
}

线性筛莫比乌斯函数

顺便求出前缀和

inline void get_mu () {
    not_prime.reset ();
    int n = maxn - 5;

    mu[1] = 1;
    not_prime[1] = true;

    for (int i = 2; i <= n; i++) {
        if (not not_prime[i]) { primes.push_back (i); mu[i] = -1; }

        for (auto p : primes) {
            if (i * p > n) { break; }
            not_prime[i * p] = true;
            mu[i * p] = -1 * mu[i];

            if (i % p == 0) { mu[i * p] = 0; break; }
        }
    }

    for (int i = 1; i <= n; i++) { sum[i] = sum[i - 1] + mu[i]; }
}   

中国剩余定理

只会大数翻倍法

orz钟神太强了%%%

inline bool merge (i64 &m1, i64 &a1, i64 m2, i64 a2) {
    if (m1 < m2) { std::swap (m1, m2); std::swap (a1, a2); }           

    while (a1 % m2 != a2) { a1 += m1; }

    m1 = lcm (m1, m2);

    return true;
}

扩展中国剩余定理

不用害怕, zhx的大数翻倍法本身就能合并两个同余方程

所以合并部分代码跟上面一样

数据结构

树状数组

template <int maxsiz>
struct BIT {
    int tr[maxsiz];

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

    inline void add (int pos, int val) { for (int i = pos; i <= n; i += lowbit(i)) { tr[i] += val; } }
    inline int _query (int pos) { int res = 0;for (int i = pos; i; i -= lowbit(i)) { res += tr[i]; } return res; }

    inline int query (int l, int r) { return _query (r) - _query (l - 1); }
};

上面只给出了单点加区间求和的版本

实际上, 树状数组还可以通过神秘的技术实现区间加单点查和 区间加区间求和

甚至可以通过跳 lowbit 实现 区间最值查询

然而实现十分复杂, 失去了树状数组本身简洁的优势

所以不如写线段树

标签:std,int,dfn,maxn,模板,now,es
From: https://www.cnblogs.com/RainbowAutomaton/p/18010693

相关文章

  • C++编程练习||1.排序函数模板2.函数模板3.重载printArray函数模板
    1.排序函数模板已知主函数如程序后缀代码所示,请为其编写适当的模板函数,使主函数的bubbleSort函数可以对一个整型数组和一个浮点数数组进行输入、排序、输出操作。#include<iostream>#include<iomanip>usingnamespacestd;template<typenameT>voidbubbleSort(T*arr,......
  • [职场] 插画师简历模板
    一份简历,应该包含求职意向、联系方式、教育背景、实践经历等信息,这些信息能够让用人单位更还得了解到求职者是一个什么样的人,能否胜任这个岗位,那一份用人单位满意的简历应该怎么写,这里就分享一份插画师的简历模板。供大家参考阅读。基本信息院校:蓝山美术学院专业:......
  • 有关各种数据结构模板
    FHQ-Treap模板题-普通平衡树#include<bits/stdc++.h>#definelstr[u].l#definerstr[u].rusingnamespacestd;constintN=3e5+10;structNode{intl,r;intkey,v;intsize;}tr[N];introot,idx;intn,m;voidpushup(intu){tr[u].size......
  • 一套模板搞定二叉树算法题--二叉树算法讲解004
    1、二叉树经典习题模拟忘记知识点和技巧时,遇到一个新的二叉树习题,该如何处理思考和写代码解题?1.1、leetcode965题目和题意:题解1成员变量self.ans:题解2递归回传:1.2、leetcode257该题是个经典二叉树题目题目和题意:题解:分析,所有路径,每一个叶子节点都需要到达。到......
  • hdu 2553 N皇后问题(DFS模板)
    Problem-2553(hdu.edu.cn)#include<iostream>#include<cstring>usingnamespacestd;intn,tot=0;intcol[12];boolcheck(intc,intr){for(inti=0;i<r;i++){if(col[i]==c||(abs(col[i]-c)==abs(i-r)))returnfalse;}r......
  • 【纯干货】如何通过技术白嫖96编辑器vip或者企业模板?
    每次公众号排版都去找免费模板,找的头都秃了,但其实好多编辑器(秀米和这个一样)都可以白嫖,教程很简单(需要简单能看懂代码(认识阿拉伯字母就行))这个96编辑器一直在用,模板超级多,基本上不用再为排版发愁了,一个模板套着弄,直接填文字内容换图片就可以,其实免费的已经够用了,但如果有VIP那就更......
  • [经验] 销售工作计划怎么写模板范文
    1、销售工作计划怎么写销售工作计划是一个销售人员在实现销售目标的过程中必不可少的工具。一个好的销售计划可以帮助销售人员更好地了解市场需求、竞争对手、产品特点等信息,并制定出具体的销售计划。那么怎么写一个好的销售工作计划呢?制定一个明确的销售目标非常重要。销售目标应......
  • Poj 1077 Eight!(BFS模板解法)
    吃完晚饭,啃着tomato来poj上提交,结果不支持unordered_map,吐血啦,看来还是要用BFS+康托展开,还想再写一篇双向BFS的,对这道题算是圆满了*_*,但是要用G++提交,C++会报错我也不知道为嘛#include<iostream>#include<cstring>#include<queue>#include<unordered_map>usingnamespaces......
  • [职场] 预算员简历模板
    个人简历基本资料姓名:蓝小小性别:男年龄:28岁籍贯:重庆现居地址:重庆渝中区政治面貌:中共党员婚姻状况:已婚求职意向意向岗位:预算员期望薪资:15K-20K意向城市:重庆到岗时间:一周之内教育背景院校名称:XXXX理工大学学历:本科就读专业:工程管理专业工作经历公司名称:蓝山地产集团有限公司就职岗位:......
  • ACM常用模板
    usefulskill连续区间求和llsum(lll,llr){return(l+r)*(r-l+1)/2;}内置位运算__builtin_ffs(x):返回x中最后一个为1的位是从后向前的第几位,如__builtin_ffs(0x789)=1,__builtin_ffs(0x78c)=3。于是,__builtin_ffs(x)-1就是x中最后一个为1的位的位置。__buil......