个人模板总结

前言

  1. 【2021.11.13】icpc济南站先对付着。

黑科技

缺省源

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int mod = 1e9 + 7;
//快读
void read(int &x) {
    int res = 0, f = 1;
    char c = getchar();
    while (!isdigit(c)) {
        if (c == '-') f = -1;
        c = getchar();
    }
    while (isdigit(c)) res = (res << 1) + (res << 3) + (c - '0'), c = getchar();
    x = res * f;
}
//快输
void OUT(int x) {
    if (x < 0) x = -x, putchar('-');
    if (x > 9) OUT(x / 10);
    putchar(x % 10 + '0');
}
void print(int a, char c) { OUT(a), putchar(c); }
//逆元
int Inv(int x) { return (x == 1) ? x : (x - x / mod) * Inv(x % mod) % mod; }
//组合数
struct Comb {
    /*后面还是要学会自己敲*/
    int re[N], inv[N], fac[N];
    void init(int n) {
        fac[0] = fac[1] = 1;
        inv[1] = 1;  //独独没有inv[0]
        re[0] = re[1] = 1;
        for (int i = 2; i <= n; i++) {
            fac[i] = fac[i - 1] * i % mod;
            inv[i] = (mod - mod / i) * inv[mod % i] % mod;
            re[i] = re[i - 1] * inv[i] % mod;
        }
    }
    int C(int a, int b) { return fac[a] * re[b] % mod * re[a - b] % mod; }
};

signed main() {}
/*

*/

暴力的艺术

在这里插入图片描述

bitset

用法合集

/*头文件*/
#include<bitset>

/*指定大小*/
bitset<1000> bs; //a bitset with 1000 bits

/*构造函数*/
bitset():每一位都是false
bitset(unsigned long val):设为val的二进制形式
bitset(const string& str):设为01串str

/*运算符*/
operator []:访问其特定的一位
operator ==/!=:比较两个bitset内容是否完全一样。
operator &/&=/|/|=/^/^=/~:进行按位与//异或/取反操作。bitset只能与bitset进行位运算,若要和整数进行位运算,
要先将整形转换为bitset。
operator <</>>/<<=/>>=:进行二进制左移/右移
operator <</>>:流运算符,这意味着你可以通过cin/cout进行输入输出。

/*成员函数*/
count():返回true的数量
size():返回bitset的大小。
test(pos):它和vector中的at()的作用是一样的,和[]运算符的区别就是越界检查。
any():若存在某一位是true则返回true,否则返回falsenone():若所有位都是false则返回true,否则返回falseall():C++11,若所有位都是true则返回false,否则返回falseset():将整个bitset设置成trueset(pos,val=true):将某一位设置成true/falsereset():将整个bitset设置成falsereset(pos):将某一位设置成false。相当于set(pos,false)flip():翻转每一位。(0<->1,相当于异或一个全是1的bitset)
flip(pos):翻转某一位。

to_string():返回转换成的字符串表达。
to_ulong():返回转换成的unsigned long表达(long在NT及32位POSIX系统下与int一样,在
64位POSIX下与long long一样)。
to_ullong():C++11,返回转换成的unsigned long long表达。

/*一写文档中没有的成员函数*/
_Find_first():返回bitset第一个true的下标,若没有true则返回bitset的大小。
_Find_next(pos):返回pos后面(下标严格大于pos的位置)第一个true的下标,若pos后面没有true则返回bitset的大小。

应用举例(与埃氏筛结合)

在这里插入图片描述

bitset<N> vis;
void Prime(int n){
    vis.set();
    vis[0]=vis[1]=0;
    for(int i=2;i*i<=n;i++){
        if(vis[i]){
            for(int j=i+i;j<=n;j+=i) vis[j]=0;
        }
    }
}

vector

v.front();
v.back();

图论

最短路

dijkstra

/*
1.题目链接:[P4779 【模板】单源最短路径(标准版)](https://www.luogu.com.cn/problem/P4779)
	a.注意是有向边 
*/
#include <bits/stdc++.h>
#define read(x) scanf("%d", &x)
#define print(a, c) printf("%d%c", a, c)
#define pb push_back
 
using namespace std;
const int maxn = 2e5 + 10;
const int inf = 2e9;
 
struct node {
    int to, w;
    node() {}
    node(int _to, int _w) { to = _to, w = _w; }
};
struct pii {
    int fi, se;
    pii() {}
    pii(int _fi, int _se) { fi = _fi, se = _se; }
    bool operator<(pii b) const { return fi > b.fi; }
};
priority_queue<pii> q;
int n, m, s;
int u, v, w;
vector<node> g[maxn];
int dis[maxn], vis[maxn];
void dijkstra(int s) {
    for (int i = 1; i <= n; i++) dis[i] = inf, vis[i] = 0;
    dis[s] = 0;
    q.push(pii(0, s));
    while (!q.empty()) {
        pii now = q.top();
        q.pop();
        int d = now.fi, i = now.se;  //距离和下标
        if (vis[i])
            continue;  //之前标记过的点仍然在优先队列中取不出来很正常,到时候取出来
        vis[i] = 1;
        for (auto j : g[i]) {
            if (vis[j.to]) continue;
            //松弛操作
            if (d + j.w < dis[j.to]) {
                dis[j.to] = d + j.w;
                q.push(pii(dis[j.to], j.to));
            }
        }
    }
}
signed main() {
    read(n), read(m), read(s);
    for (int i = 1; i <= m; i++) {
        read(u), read(v), read(w);
        g[u].pb(node(v, w));
    }
    dijkstra(s);
    for (int i = 1; i <= n; i++) print(dis[i], ' ');
    return 0;
}

SPFA(放入队列中的同时vis[s]=1,取出的同时vis[now]=0,更新所有可以更新的,更新的时候放入所有能放入的即vis[x]=0的x)

/*
1.题目链接:(和上面一个题)
*/
#include <bits/stdc++.h>
#define read(x) scanf("%d", &x)
#define print(a, c) printf("%d%c", a, c)
#define pb push_back
 
using namespace std;
const int maxn = 2e5 + 10;
const int inf = 2e9;
 
struct node {
    int to, w;
    node() {}
    node(int _to, int _w) { to = _to, w = _w; }
};
int n, m, s;
int u, v, w;
vector<node> g[maxn];
int dis[maxn], vis[maxn];
void spfa() {
    for (int i = 1; i <= n; i++) dis[i] = inf, vis[i] = 0;
    dis[s] = 0;
    queue<int> q;
    q.push(s), vis[s] = 1;
    while (!q.empty()) {
        int x = q.front();
        q.pop(), vis[x] = 0;  //毕竟一般也不会有自环
        for (auto i : g[x]) {
            /* if
             * (vis[i.to])continue;不能这样,这个算法在队列中不代表不能够优化!——应该放在下面!*/
            if (dis[x] + i.w < dis[i.to]) {
                dis[i.to] = dis[x] + i.w;
                if (!vis[i.to]) q.push(i.to), vis[i.to] = 1;
            }
        }
    }
}
signed main() {
    read(n), read(m), read(s);
    for (int i = 1; i <= m; i++) {
        read(u), read(v), read(w);
        g[u].pb(node(v, w));
    }
    spfa();
    for (int i = 1; i <= n; i++) print(dis[i], ' ');
    return 0;
}

Floyd

for (int k = 1; k <= n; k++)
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++) g[i][j] = min(g[i][j], g[i][k] + g[k][j]);

最短路径树(应用)

  1. 概念:最短路构成的一棵树(一个概念罢了,拓展学不是很强)。

最短路径树计数

  1. 题目链接黑暗城堡 LibreOJ - 10064
  2. 题意 n ( 1 ≤ n ≤ 1000 ) n(1\le n\le 1000) n(1n1000)个点,m条无向边,每条边长度为 l i ( 1 ≤ l ≤ 200 ) l_i(1\le l\le200) li1l200,求不同最短路径树(最开始从1出发)的数量(结果对 2 31 − 1 2^{31}-1 2311取余)。
  3. 题解:求出每个点的前一个节点的数量(满足对短路,即dis[u]+w==dis[v],dis[i]表示节点s到i的最短距离),最后相乘取余。
  4. 操作:在松弛操作的时候操作(spfa的话建议用第二种操作,第一种或多或少有些问题!)或者在求出所有点的最短路之后操作。
模板(最短路径数计数模板题):
/*
1.题目就是上面的【黑暗城堡】
*/
/*操作1:在松弛操作的地方做修改*/
#include <bits/stdc++.h>
#define int long long
#define read(x) scanf("%lld", &x)
#define print(a, c) printf("%lld%c", a, c)
// #define ll long long
#define pb push_back
 
using namespace std;
const int maxn = 1e3 + 10;
const int maxm = 1e6 + 10;
const int inf = 2147483647;  // 1<<31-1
 
struct node {
    int to, w;
    node() {}
    node(int _to, int _w) { to = _to, w = _w; }
};
struct pii {
    int fi, se;
    pii() {}
    pii(int _fi, int _se) { fi = _fi, se = _se; }
    bool operator<(pii b) const { return fi > b.fi; }
};
priority_queue<pii> q;
int n, m;
int u, v, w;
vector<node> g[maxn];
int dis[maxn], vis[maxn], pre[maxn];
void dijkstra() {
    for (int i = 1; i <= n; i++) dis[i] = inf, vis[i] = 0, pre[i] = 0;
    dis[1] = 0, pre[1] = 1;
    q.push(pii(0, 1));
    while (!q.empty()) {
        pii now = q.top();
        q.pop();
        int d = now.fi, i = now.se;  //距离和下标
        if (vis[i])
            continue;  //之前标记过的点仍然在优先队列中取不出来很正常,到时候取出来
        vis[i] = 1;
        for (auto j : g[i]) {
            if (vis[j.to]) continue;
            //松弛操作
            if (d + j.w == dis[j.to]) pre[j.to]++;  //操作1第一步
            if (d + j.w < dis[j.to]) {
                dis[j.to] = d + j.w;
                q.push(pii(dis[j.to], j.to)), pre[j.to] = 1;  //操作一第二步
            }
        }
    }
}
signed main() {
    read(n), read(m);
    for (int i = 1; i <= m; i++) {
        read(u), read(v), read(w);
        g[u].pb(node(v, w)), g[v].pb(node(u, w));
    }
    dijkstra();
    int ans = 1;
    // for (int i = 1; i <= n; i++) cout << i << ":::" << pre[i] << endl;
    for (int i = 1; i <= n; i++) ans = (ans * pre[i]) % inf;
    print(ans, '\n');
    return 0;
}

去掉途中一条边之后最短路径树大小(dis之和)是否有变化

  1. 题意 n n n个城市 ( n ≤ 100 ) (n\le 100) n100 m m m条无向边,每两个城市之间都走最小路。从小到大打印所有公路序号,这些公路满足:去掉之后至少有两个城市之间的路程变大了。
  2. 题解:暴力每个起点,分别建最短路径树,然后枚举这树上的每条边,不经过这条边建一颗最短路径树,求出该起点到其他每个点的最短路径和,如果大于没去掉这条边之前的值,一定满足条件。说明,枚举任意起点的时候可以建任意一颗最短路径树,因为如果存在多颗最短路径树,同时枚举的某条边如果可以替代,那么他们是可以相互替代的,去掉任何一颗都不会改变最短路径和!!!
  3. 代码:就附上了,知道思路即可,做法仍是最短路+建最短路径树。

暂时就以上两个应用吧,毕竟自己没遇到过

最短路计数

  1. 概念:字面意思。
  2. 操作:与最短路径树的操作几乎一样,只需要做一些改动:pre初始化为0;pre[s]=1;然后每次遇到dis[u]==dis[v]的时候pre[v]+=pre[u];松弛操作的时候pre[v]=pre[u]就ok了。
  3. 对比最短路径树:pre初始化为0;pre[s]=1;然后每次遇到dis[u]==dis[v]的时候pre[v]++;松弛操作的时候pre[v]=1就ok了。
  4. 例题P1608 路径统计
    1. 题意:n 个点,最开始在1,要到达n。问最短路径是多少,有多少条。如果一条都没有,就打印no。 n ≤ 2000 , m ≤ n ∗ ( n − 1 ) n\le 2000,m\le n*(n-1) n2000,mn(n1),保证无自环,不保证无重边,另外所有路是单向的!——重边取最小值,而不是如果想等的有 x 条就表示这里有 x 种走法。

模板

void dijkstra() {
    for (int i = 1; i <= n; i++) dis[i] = inf, vis[i] = 0, pre[i] = 0;
    dis[1] = 0, pre[1] = 1;
    q.push(pii(0, 1));  //小顶堆
    while (!q.empty()) {
        pii now = q.top();
        q.pop();
        int d = now.fi, i = now.se;  //距离和下标
        if (vis[i])
            continue;  //之前标记过的点仍然在优先队列中取不出来很正常,到时候取出来
        vis[i] = 1;
        for (auto j : g[i]) {
            if (vis[j.to]) continue;
            //松弛操作
            if (d + j.w == dis[j.to]) pre[j.to] += pre[i];  //操作一第一步
            if (d + j.w < dis[j.to]) {
                dis[j.to] = d + j.w;
                q.push(pii(dis[j.to], j.to)),
                    pre[j.to] = pre[i];  //操作一第二步
            }
        }
    }
    // dis[i]==inf,或者说pre[n]=0时表示无路径
    (dis[n] == inf) ? puts("No answer")
                    : (print(dis[n], ' '), print(pre[n], '\n'));
}

分层图最短路(用分层图的几种情况)

  1. 参考博客
    1. 图论之分层图最短路总结 与经典例题
    2. 专题·最短路【including Dijkstra, SPFA,Floyd,传递闭包,二维最短路,分层图最短路,最短路计数……
  2. 介绍:只是在建图的时候不一样,除此之外就是跑个普通最短路。当然,难的也就是建图。

用分层图的几种情况

  1. k 个不同集合的边。将每个集合建一层图,然后第 k+1 层建一个虚图(用于连接每一层,价值根据题意)。如小雨坐地铁。

  2. 有 k 个机会免费走一条路。建 k+1 个相同的图,每层之间用有边的点连接起来,代价为0。没走一层表示用了一次机会。如P4568 [JLOI2011]飞行路线。

  3. 有k个机会逆向行驶。建 k+1 个相同的图,每层之间用有边的两点之间的逆向边连接,权值不变,没走一层表示用了一次机会(这里应该必须是有向图罢,不然没啥意义)。

例题1:小雨坐地铁
  1. 题目描述 n ( 1 − 1000 ) n(1-1000) n11000个车站, m ( 1 − 500 ) m(1-500) m1500条地铁线。每次经过第 i 条地铁线需要花费 a i ( 1 − 100 ) a_i(1-100) ai1100,在这条地铁线上,每经过一个车站多花费 b i ( 1 − 100 ) b_i(1-100) bi1100,每条线上有 c i ( 1 − n ) c_i(1-n) ci1n个车站(按顺序依次输入沿途站,注意是双向的)。
  2. 题解:情况1。注意进入第 m+1 层的时候花费 0 ,从 m+1 层进入的时候才花费 a x ax ax(相当于从nm+s到nm+t)。多读题,图别检错;注意建图之后点的总数。
  3. 代码
#include <bits/stdc++.h>
#define int long long
#define read(x) scanf("%lld", &x)
#define print(a, c) printf("%lld%c", a, c)
#define pb push_back
#define mst(a, x) memset(a, x, sizeof(a))
#define dbg(x) cout << #x << "===" << x << endl
using namespace std;
const int maxn = 500 * 1000 + 1000 + 10;
const int mod = 1e9 + 7;
const int inf = 1e9;
struct node {
    int to, w;
    node() {}
    node(int _to, int _w) { to = _to, w = _w; }
};
struct pii {
    int fi, se;  // dis,id
    pii() {}
    pii(int _fi, int _se) { fi = _fi, se = _se; }
    bool operator<(pii b) const {
        return fi > b.fi;
    }  //平常的就大顶堆,否则小顶堆。这里小顶堆
};
int n, m, s, t;
int a, b, c;
vector<node> g[maxn];
int dis[maxn], vis[maxn];
priority_queue<pii> q;
 
void dijkstra(int s) {
    for (int i = 1; i <= n * m + n; i++) dis[i] = inf;
    dis[s] = 0, vis[s] = 1;
    q.push(pii(0, s));
    while (!q.empty()) {
        pii now = q.top();
        q.pop();
        int x = now.se, w = now.fi;
        for (auto i : g[x]) {
            if (i.w + dis[x] < dis[i.to]) {
                dis[i.to] = i.w + dis[x], vis[i.to] = 1;
                q.push(pii(dis[i.to], i.to));
            }
        }
    }
}
signed main() {
    read(n), read(m), read(s), read(t);
    for (int i = 1; i <= m; i++) {
        read(a), read(b), read(c);
        int now, pre;
        //建第k张图
        for (int j = 1; j <= c; j++) {
            read(now);
            if (j > 1) {
                int u = (i - 1) * n + pre, v = (i - 1) * n + now, w = b;
                g[u].pb(node(v, w));
                g[v].pb(node(u, w));
            }
            //连虚图(相当于中转站)
            int u = (i - 1) * n + now, v = n * m + now, w = a;
            g[u].pb(node(v, 0));  //进入需要0
            g[v].pb(node(u, w));  //出来代表从一条线进入新的一条线(每次都要花钱)
            pre = now;
        }
    }
    dijkstra(n * m + s);  //从n*m+t出来
    // for (int i = 1; i <= 10; i++) print(dis[i], ' ');
    // cout << endl;
    if (dis[n * m + t] == inf)
        puts("-1");
    else
        print(dis[n * m + t], '\n');
    return 0;
}
题目2:P4568 [JLOI2011]飞行路线
  1. 题目链接P4568 [JLOI2011]飞行路线
  2. 题意 n ( 2 − 1 e 4 ) n(2-1e4) n21e4个点, m ( 1 − 5 e 4 ) m(1-5e4) m15e4条无向带权边(权值大小在 0 − 1 e 3 0-1e3 01e3),可以最多坐 k ( 0 − 10 ) k(0-10) k010次免费航班。问从 s 到 t 的最小花费。
  3. 题解:情况2 。难点在建图上
  4. 代码
#include <bits/stdc++.h>
#define int long long
#define read(x) scanf("%lld", &x)
#define print(a, c) printf("%lld%c", a, c)
#define pb push_back
#define mst(a, x) memset(a, x, sizeof(a))
#define dbg(x) cout << #x << "===" << x << endl
using namespace std;
const int maxn = 1e4 * 12 + +10;
const int mod = 1e9 + 7;
const int inf = 1e9;
struct node {
    int to, w;
    node() {}
    node(int _to, int _w) { to = _to, w = _w; }
};
struct pii {
    int fi, se;  // dis,id
    pii() {}
    pii(int _fi, int _se) { fi = _fi, se = _se; }
    bool operator<(pii b) const {
        return fi > b.fi;
    }  //正常为大顶堆,不正常为小顶堆
};
int n, m, k, s, t;
int u, v, w;
vector<node> g[maxn];
int dis[maxn], vis[maxn];
priority_queue<pii> q;
 
void dijkstra(int s) {
    for (int i = 0; i <= n * k + n; i++) dis[i] = inf;
    dis[s] = 0, vis[s] = 1;
    q.push(pii(0, s));
    while (!q.empty()) {
        pii now = q.top();
        q.pop();
        int x = now.se, w = now.fi;
        for (auto i : g[x]) {
            if (i.w + dis[x] < dis[i.to]) {
                dis[i.to] = i.w + dis[x], vis[i.to] = 1;
                q.push(pii(dis[i.to], i.to));
            }
        }
    }
}
signed main() {
    read(n), read(m), read(k);
    read(s), read(t);
    for (int i = 1; i <= m; i++) {
        read(u), read(v), read(w);
        // 0层图
        g[u].pb(node(v, w)), g[v].pb(node(u, w));
        for (int j = 1; j <= k; j++) {
            // 1~k层图
            g[j * n + u].pb(node(j * n + v, w));
            g[j * n + v].pb(node(j * n + u, w));
            //上一层与这一层连接
            g[(j - 1) * n + u].pb(node(j * n + v, 0));
            g[(j - 1) * n + v].pb(node(j * n + u, 0));
        }
    }
    //传到n*k+t,最后直接取dis[n*k+t];
    for (int i = 1; i <= k; i++) g[(i - 1) * n + t].pb(node(i * n + t, 0));
    dijkstra(s);  //从n*m+t出来
    // for (int i = 0; i <= 10; i++) print(dis[i], ' ');
    // cout << endl;
    print(dis[n * k + t], '\n');
    return 0;
}
/*
5 6 1
0 4
0 1 5
1 2 5
2 3 5
3 4 5
2 3 3
0 2 100
*/
情况3:与情况2的差别只在建图,有这个意识就好。
  1. ps:也不止与这些应用。

k短路(模板题)

  1. 题目链接Remmarguts’ Date POJ - 2449
  2. 题意:N ( 1 ≤ N ≤ 1000 ) (1\le N\le 1000) (1N1000)个点,M ( 1 ≤ M ≤ 1 e 5 ) (1\le M\le 1e5) (1M1e5)条有向带权边,问点 s 到 t 的第 k $ ( 1 ≤ k ≤ 1000 ) (1\le k\le 1000) (1k1000)短路的长度。
  3. 题解k短路模板题。首先建一个反向路,dis[i]表示到 t 的最短路,然后 bfs ,得到一次 t 记一次数。bfs 具体操作见代码。
  4. 注意
    1. s 可能到不了 t。
    2. s 到 t可能没有 k 条路(s-t的任意路径上有环的话,s 到 t 的路可以视作有无数条)。
    3. s==t的时候 k 要加上1 ,因为 bfs 的时候最开始的 s 被算作 t(长度为 0 )。

模板1

#include <string.h>
 
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <vector>
// #define int long long
#define read(x) scanf("%d", &x)
#define print(a, c) printf("%d%c", a, c)
#define pb push_back
#define mst(a, x) memset(a, x, sizeof(a))
#define dbg(x) cout << #x << "===" << x << endl
using namespace std;
const int maxn = 1e3 + 10;
const int mod = 1e9 + 7;
const int inf = 1e9;
struct node {
    int to, w;
    node() {}
    node(int _to, int _w) { to = _to, w = _w; }
};
struct pii {
    int i, dis, dis1;
    pii() {}
    pii(int _i, int _dis, int _dis1) { i = _i, dis = _dis, dis1 = _dis1; }
    bool operator<(pii b) const { return dis + dis1 > b.dis + b.dis1; }
};
 
int n, m, s, t, k;
int u, v, w;
vector<node> g[maxn], re[maxn];
int dis[maxn], vis[maxn];
void spfa(int s) {
    for (int i = 0; i <= n; i++) dis[i] = inf;
    dis[s] = 0;
    queue<int> q;
    q.push(s), vis[s] = 1;
    while (!q.empty()) {
        int x = q.front();
        q.pop();
        vis[x] = 0;
        // sbpoj,c++11都不支持
        for (int j = 0; j < re[x].size(); j++) {
            node i = re[x][j];
            if (i.w + dis[x] < dis[i.to]) {
                dis[i.to] = i.w + dis[x];
                if (vis[i.to] == 0) q.push(i.to), vis[i.to] = 1;
            }
        }
    }
}
priority_queue<pii> q;
void bfs(int s) {
    q.push(pii(s, 0, 0));
    int cnt = 0;
    while (!q.empty()) {
        pii now = q.top();
        q.pop();
        if (now.i == t) {
            cnt++;
            if (cnt == k) {
                print(now.dis, '\n');
                return;
            }
        }
        for (int j = 0; j < g[now.i].size(); j++) {
            node i = g[now.i][j];
            q.push(pii(i.to, now.dis + i.w, dis[i.to]));
        }
    }
    puts("-1");  //这是什么情况?——毕竟单向路径,可能s->t都没有k条路
}
signed main() {
    read(n), read(m);
    for (int i = 1; i <= m; i++) {
        read(u), read(v), read(w);
        g[u].pb(node(v, w));
        re[v].pb(node(u, w));
    }
    read(s), read(t), read(k);
    spfa(t);
    if (dis[s] == inf)
        puts("-1");
    else {
        if (s == t) k++;  //为啥。看bfs哪里。s也被视作t了,这个t不算
        bfs(s);
    }
    return 0;
}
/*
input:::
2 2
1 2 5
2 1 4
1 2 2
output:::
14
*/

模板2(要求输出前 k 小的数的话)

#include <bits/stdc++.h>
// #define int long long
#define read(x) scanf("%d", &x)
#define print(a, c) printf("%d%c", a, c)
#define pb push_back
#define mst(a, x) memset(a, x, sizeof(a))
#define dbg(x) cout << #x << "===" << x << endl
using namespace std;
const int maxn = 1e3 + 10;
const int mod = 1e9 + 7;
const int inf = 1e9;
struct node {
    int to, w;
    node() {}
    node(int _to, int _w) { to = _to, w = _w; }
};
struct pii {
    int i, dis, dis1;
    pii() {}
    pii(int _i, int _dis, int _dis1) { i = _i, dis = _dis, dis1 = _dis1; }
    bool operator<(pii b) const { return dis + dis1 > b.dis + b.dis1; }
};
 
int n, m, k;
int u, v, w;
vector<node> g[maxn], re[maxn];
vector<int> ans;
int dis[maxn], vis[maxn];
void spfa(int s) {
    for (int i = 0; i <= n; i++) dis[i] = inf;
    dis[s] = 0;
    queue<int> q;
    q.push(s), vis[s] = 1;
    while (!q.empty()) {
        int x = q.front();
        q.pop();
        vis[x] = 0;
        for (int j = 0; j < re[x].size(); j++) {
            node i = re[x][j];
            if (i.w + dis[x] < dis[i.to]) {
                dis[i.to] = i.w + dis[x];
                if (vis[i.to] == 0) q.push(i.to), vis[i.to] = 1;
            }
        }
    }
}
priority_queue<pii> q;
void bfs(int s) {
    q.push(pii(s, 0, 0));
    int cnt = 0;
    while (!q.empty()) {
        pii now = q.top();
        q.pop();
        if (now.i == 1) {
            cnt++;
            ans.pb(now.dis);
            if (cnt == k) return;
        }
        for (int j = 0; j < g[now.i].size(); j++) {
            node i = g[now.i][j];
            q.push(pii(i.to, now.dis + i.w, dis[i.to]));
        }
    }
    while (ans.size() < k) ans.pb(-1);
}
signed main() {
    read(n), read(m), read(k);
    for (int i = 1; i <= m; i++) {
        read(u), read(v), read(w);
        g[u].pb(node(v, w));
        re[v].pb(node(u, w));
    }
    spfa(1);
    if (dis[n] == inf)
        for (int i = 1; i <= k; i++) ans.pb(-1);
    else {
        if (n == 1) k++;  //为啥。看bfs哪里。s也被视作t了,这个t不算
        bfs(n);
    }
    for (auto i : ans) print(i, '\n');
    return 0;
}
/*
input:::
5 8 7 
5 4 1 
5 3 1 
5 2 1 
5 1 1 
4 3 4 
3 1 1 
3 2 1 
2 1 1 
output:::
1 
2 
2 
3 
6 
7 
-1 
*/

最小生成树(配合kuangbin板子使用)

在这里插入图片描述

网络流

模板1:链式前向星

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int M = 1e5 + 10;

int cnt, head[N];  // cnt初始化为0,head初始化为-1
//结构体
struct Edge {
    int to, w, next;
    Edge(int to = 0, int w = 0, int next = 0) : to(to), w(w), next(next) {}
} edge[M];
//加边
void add(int u, int v, int w) {
    edge[cnt] = Edge(v, w, head[u]), head[u] = cnt++;  //正向边
    edge[cnt] = Edge(u, w, head[v]), head[v] = cnt++;
    //反向边(注意权值,按题目要求)
    /*    建议全部从0开始,网络流那里从1开始好像就有问题cnt从1开始:edge[++cnt]=Edge(v,w,head[u]),head[u]=cnt;*/
}
//遍历:for(int i=head[u];~i;i=edge[i].next)
void init() { memset(head, 0, sizeof(head)), cnt = 0; }

signed main() {
    init();
    return 0;
}

模板2:Dinic(Dinic应该是最常用到的,比ISAP慢不了多少)

//题目仍然是上面EK的那个题目。
#include <bits/stdc++.h>

// #define int long long
#define read(x) scanf("%d", &x)
#define print(a, c) printf("%d%c", a, c)
#define pb push_back
#define dbg(x) cout << #x << "===" << x << endl
using namespace std;
const int M = 2e2 + 10;
const int INF = 1e9 + 10;

struct Edge {
    int to, w, next;
    Edge(int to = 0, int w = 0, int next = 0) : to(to), w(w), next(next) {}
} e[M << 1];
int n, m, a, b, c;
int s, t;
int head[M], cnt;
void add(int u, int v, int w) {
    e[cnt] = Edge(v, w, head[u]), head[u] = cnt++;
    e[cnt] = Edge(u, 0, head[v]), head[v] = cnt++;
}
int dep[M], now[M];
// bfs使dfs按照dep来增广,得到优化(具体证明就看不太懂了)。
bool bfs() {
    memset(dep, -1, sizeof(dep));
    queue<int> q;
    q.push(s), dep[s] = 0;
    now[s] = head[s];  //当前弧优化(初始化)
    while (!q.empty()) {
        int x = q.front();
        q.pop();
        for (int i = now[x]; ~i; i = e[i].next) {
            int v = e[i].to;
            if (dep[v] == -1 && e[i].w > 0) {
                dep[v] = dep[x] + 1;
                now[v] = head[v];  //当前弧优化(初始化)。
                q.push(v);
                if (v == t) return true;
                //遇到v==t即退出,因为<dep[v]的已经遍历完了,dfs的增广路不可能经过深度>=dep[v]的点
            }
        }
    }
    return false;
}
//一次dfs可能有几条增广路:见图
int dfs(int x, int flow) {
    if (x == t) return flow;
    int ans = 0;
    // flow表示x点还能操作的流量,ans表示增广的这几路后流出x总最大流
    // flow不剩下了,就没必要增广了
    for (int i = now[x]; ~i && flow; i = e[i].next) {
        int v = e[i].to;
        now[x] = i;
        //当前弧优化(这里才能得到优化,bfs中的只是初始化:另now[x]=head[x])
        if (dep[v] == dep[x] + 1 && e[i].w > 0) {
            int tmp = dfs(v, min(flow, e[i].w));
            // tmp为流过点v的最大流量
            if (tmp == 0) dep[v] = -1;  //剪枝,去掉增广完毕的点
            e[i].w -= tmp;
            e[i ^ 1].w += tmp;
            ans += tmp;
            flow -= tmp;  //流过当前点(x)的流量累加(回溯)
        }
    }
    return ans;  //最后返回的是这几路增广的流量和
}
int Dinic() {
    int maxflow = 0;
    // bfs为真就一直dfs多路增广
    while (bfs()) maxflow += dfs(s, INF);
    return maxflow;
}
signed main() {
    read(n), read(m);
    memset(head, -1, sizeof(head));
    s = 1, t = m;
    for (int i = 1; i <= n; i++) {
        read(a), read(b), read(c);
        add(a, b, c);
    }
    print(Dinic(), '\n');
    return 0;
}

ISAP(终于进入正题了?&比Dinic快的板子)

// Luogu _P2740 [USACO4.2]草地排水Drainage Ditches
#include <bits/stdc++.h>
// #define int long long
#define read(x) scanf("%d", &x)
#define print(a, c) printf("%d%c", a, c)
#define dbg(x) cout << #x << "===" << x << endl
using namespace std;
const int N = 2e2 + 10;
const int M = 4e2 + 10;  //还是直接放在这里操作更舒服 M<<1多少容易弄错
const int INF = 1e9 + 7;
int n, m, s, t, a, b, c;
struct Edge {
    int v, w, next;
    Edge(int v = 0, int w = 0, int next = 0) : v(v), w(w), next(next) {}
} e[M];
int head[N], cnt, now[N];
void add(int u, int v, int w) {
    e[cnt] = Edge(v, w, head[u]), head[u] = cnt++;
    e[cnt] = Edge(u, 0, head[v]), head[v] = cnt++;
}
int dep[N], gap[N];  // gap优化:貌似优化力度挺大的
void bfs() {
    for (int i = 0; i <= n; i++) dep[i] = gap[i] = 0, now[i] = head[i];
    queue<int> q;
    q.push(t), dep[t] = 1, gap[dep[t]]++;
    while (!q.empty()) {
        int x = q.front();
        q.pop();
        for (int i = head[x]; ~i; i = e[i].next) {
            int v = e[i].v;
            if (!dep[v]) dep[v] = dep[x] + 1, q.push(v), gap[dep[v]]++;
        }
    }
}
int dfs(int x, int flow) {
    if (x == t) return flow;
    int ans = 0;
    for (int i = now[x]; ~i; i = e[i].next) {
        int v = e[i].v;
        now[x] =
            i;  //怎么总是忘了弧优化的操作?甚至i=head[i]都来了?唉,还是太菜了
        if (dep[x] == dep[v] + 1) {
            int tmp = dfs(v, min(flow, e[i].w));
            //对比Dinic,这里没有tmp==0则dep[v]=INF。如果有这里的剪枝,很明显就破坏了dep的值。就需要从新bfs更新dep了
            e[i].w -= tmp;
            e[i ^ 1].w += tmp;
            ans += tmp;
            flow -= tmp;
            if (!flow) return ans;
            // x没有跑完,不能够像Dinic一样直接跳出去返回ans,v点是跑完了,但是上一层dfs已经修改了dep[v]
            // Dinic是~i&&flow。其实Dinic也可以像这里一样写
        }
    }
    //上面的操作与Dinic几乎一模一样(与Dinic还是有一点差别的,没有剪枝!!)
    //以下理解来源于https://www.luogu.com.cn/blog/ONE-PIECE/jiu-ji-di-zui-tai-liu-suan-fa-isap-yu-hlpp
    //到这里的时候,x流出去的点已经全部流过了
    //但是从前面流过的点还有剩余。更改dep使x与x流出的点分开
    // emmm,虽然没有完全理解,但是知道怎么操作,虽然知道怎么操作,但是没有完全理解
    //过一段时间再来看看
    gap[dep[x]]--;  //为什莫是这个地方dep[x]++?
    if (gap[dep[x]] == 0) dep[s] = n + 1;
    ++gap[++dep[x]], now[x] = head[x];  //当前弧优化,在每次分层之后都要回到head
    return ans;
}
int ISAP() {
    int res = 0;
    bfs();
    // dbg(dep[s]);bfs有没有错误
    while (dep[s] <= n && dep[s]) res += dfs(s, INF);
    return res;
}
void init() {
    memset(head, -1, sizeof(head)), cnt = 0;
    read(m), read(n);  // m为边数,n为点数
    s = 1, t = n;
    for (int i = 1; i <= m; i++) {
        read(a), read(b), read(c);
        add(a, b, c);
    }
}
signed main() {
    init();
    int maxflow = ISAP();
    print(maxflow, '\n');
    return 0;
}

单独打印(大概率是常用版本——不局限于icpc济南):

数论

数论基础总结

图论——树链剖分&倍增求LCA

树链剖分学习&总结

数据结构

数据结构总结

字符串

字符串学习&总结(感觉主要是总结模板)

计算几何

旋转卡壳&凸包(不用一下子就学完所有)

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值