目录
前言
- 【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,否则返回false。
none():若所有位都是false则返回true,否则返回false。
all():C++11,若所有位都是true则返回false,否则返回false。
set():将整个bitset设置成true。
set(pos,val=true):将某一位设置成true/false。
reset():将整个bitset设置成false。
reset(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]);
最短路径树(应用)
- 概念:最短路构成的一棵树(一个概念罢了,拓展学不是很强)。
最短路径树计数
- 题目链接:黑暗城堡 LibreOJ - 10064
- 题意: n ( 1 ≤ n ≤ 1000 ) n(1\le n\le 1000) n(1≤n≤1000)个点,m条无向边,每条边长度为 l i ( 1 ≤ l ≤ 200 ) l_i(1\le l\le200) li(1≤l≤200),求不同最短路径树(最开始从1出发)的数量(结果对 2 31 − 1 2^{31}-1 231−1取余)。
- 题解:求出每个点的前一个节点的数量(满足对短路,即
dis[u]+w==dis[v]
,dis[i]表示节点s到i的最短距离),最后相乘取余。 - 操作:在松弛操作的时候操作(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之和)是否有变化
- 题意: n n n个城市 ( n ≤ 100 ) (n\le 100) (n≤100), m m m条无向边,每两个城市之间都走最小路。从小到大打印所有公路序号,这些公路满足:去掉之后至少有两个城市之间的路程变大了。
- 题解:暴力每个起点,分别建最短路径树,然后枚举这树上的每条边,不经过这条边建一颗最短路径树,求出该起点到其他每个点的最短路径和,如果大于没去掉这条边之前的值,一定满足条件。说明,枚举任意起点的时候可以建任意一颗最短路径树,因为如果存在多颗最短路径树,同时枚举的某条边如果可以替代,那么他们是可以相互替代的,去掉任何一颗都不会改变最短路径和!!!
- 代码:就附上了,知道思路即可,做法仍是最短路+建最短路径树。
暂时就以上两个应用吧,毕竟自己没遇到过
最短路计数
- 概念:字面意思。
- 操作:与最短路径树的操作几乎一样,只需要做一些改动:pre初始化为0;
pre[s]=1
;然后每次遇到dis[u]==dis[v]
的时候pre[v]+=pre[u]
;松弛操作的时候pre[v]=pre[u]
就ok了。 - 对比最短路径树:pre初始化为0;
pre[s]=1
;然后每次遇到dis[u]==dis[v]
的时候pre[v]++
;松弛操作的时候pre[v]=1
就ok了。 - 例题:P1608 路径统计
- 题意:n 个点,最开始在1,要到达n。问最短路径是多少,有多少条。如果一条都没有,就打印no。 n ≤ 2000 , m ≤ n ∗ ( n − 1 ) n\le 2000,m\le n*(n-1) n≤2000,m≤n∗(n−1),保证无自环,不保证无重边,另外所有路是单向的!——重边取最小值,而不是如果想等的有 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'));
}
分层图最短路(用分层图的几种情况)
- 参考博客:
- 介绍:只是在建图的时候不一样,除此之外就是跑个普通最短路。当然,难的也就是建图。
用分层图的几种情况
-
k 个不同集合的边。将每个集合建一层图,然后第 k+1 层建一个虚图(用于连接每一层,价值根据题意)。如小雨坐地铁。
-
有 k 个机会免费走一条路。建 k+1 个相同的图,每层之间用有边的点连接起来,代价为0。没走一层表示用了一次机会。如P4568 [JLOI2011]飞行路线。
-
有k个机会逆向行驶。建 k+1 个相同的图,每层之间用有边的两点之间的逆向边连接,权值不变,没走一层表示用了一次机会(这里应该必须是有向图罢,不然没啥意义)。
例题1:小雨坐地铁
- 题目描述: n ( 1 − 1000 ) n(1-1000) n(1−1000)个车站, m ( 1 − 500 ) m(1-500) m(1−500)条地铁线。每次经过第 i 条地铁线需要花费 a i ( 1 − 100 ) a_i(1-100) ai(1−100),在这条地铁线上,每经过一个车站多花费 b i ( 1 − 100 ) b_i(1-100) bi(1−100),每条线上有 c i ( 1 − n ) c_i(1-n) ci(1−n)个车站(按顺序依次输入沿途站,注意是双向的)。
- 题解:情况1。注意进入第 m+1 层的时候花费 0 ,从 m+1 层进入的时候才花费 a x ax ax(相当于从nm+s到nm+t)。多读题,图别检错;注意建图之后点的总数。
- 代码:
#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]飞行路线
- 题目链接:P4568 [JLOI2011]飞行路线
- 题意: n ( 2 − 1 e 4 ) n(2-1e4) n(2−1e4)个点, m ( 1 − 5 e 4 ) m(1-5e4) m(1−5e4)条无向带权边(权值大小在 0 − 1 e 3 0-1e3 0−1e3),可以最多坐 k ( 0 − 10 ) k(0-10) k(0−10)次免费航班。问从 s 到 t 的最小花费。
- 题解:情况2 。难点在建图上
- 代码:
#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的差别只在建图,有这个意识就好。
- ps:也不止与这些应用。
k短路(模板题)
- 题目链接:Remmarguts’ Date POJ - 2449
- 题意:N ( 1 ≤ N ≤ 1000 ) (1\le N\le 1000) (1≤N≤1000)个点,M ( 1 ≤ M ≤ 1 e 5 ) (1\le M\le 1e5) (1≤M≤1e5)条有向带权边,问点 s 到 t 的第 k $ ( 1 ≤ k ≤ 1000 ) (1\le k\le 1000) (1≤k≤1000)短路的长度。
- 题解:k短路模板题。首先建一个反向路,
dis[i]
表示到 t 的最短路,然后 bfs ,得到一次 t 记一次数。bfs 具体操作见代码。 - 注意:
- s 可能到不了 t。
- s 到 t可能没有 k 条路(s-t的任意路径上有环的话,s 到 t 的路可以视作有无数条)。
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;
}