【模板】图论
图的储存
链式前向星
void add(int x, int y, int z){
to[++e] = y; nxt[e] = begin[x]; w[e] = z; begin[x] = e;
// to[++e] = x; nxt[e] = begin[y]; w[e] = z; begin[y] = e; 无向图
}
//遍历
当前节点为u
for(int i = begin[u]; i; i = nxt[i]){
int v = to[i], w_ = w[i];
}
(以下复杂度对比来自网络,侵权即删!)
Dijkstra:O(n2)适用于权值为非负的图的单源最短路径,用斐波那契堆的复杂度O(E+VlgV),
BellmanFord:适用于权值有负值的图的单源最短路径,并且能够检测负圈,复杂度O(VE)
SPFA:适用于权值有负值,且没有负圈的图的单源最短路径,论文中的复杂度O(kE),k为每个节点进入Queue的次数,且k一般<=2,但此处的复杂度证明是有问题的,其实SPFA的最坏情况应该是O(VE).
Floyd:每对节点之间的最短路径。Floyd-Warshall算法的时间复杂度为O(N3),空间复杂度为O(N2)。
先给出结论:
(1)当权值为非负时,用Dijkstra。
(2)当权值有负值,且没有负圈,则用SPFA,SPFA能检测负圈,但是不能输出负圈。
(3)当权值有负值,而且可能存在负圈,则用BellmanFord,能够检测并输出负圈。
(4)SPFA检测负环:当存在一个点入队大于等于V次,则有负环。
最短路
dijkstra(朴素)
#include<bits/stdc++.h>
using namespace std;
const int oo = 2147483647;
const int N = 10010;
const int M = 500010;
int n, m, s, e;
int dis[N], vis[N];
int to[M*2], nxt[M*2], wht[M*2], begin[N];
template <typename T>
T read(){
T N(0), F(1);
char C = getchar();
for(; !isdigit(C); C = getchar()) if(C == '-') F = -1;
for(; isdigit(C); C = getchar()) N = N*10 + C-48;
return N*F;
}
void add(int x, int y, int z){
to[++e] = y; nxt[e] = begin[x]; wht[e] = z; begin[x] = e;
// to[++e] = x; nxt[e] = begin[y]; wht[e] = z; begin[y] = e;
}
void dijkstra(int st){
for(int i = 1; i <= n; i++){
int mx = oo, u = -1;
for(int j = 1; j <= n; j++){
if(!vis[j] && dis[j] < mx){
mx = dis[j];
u = j;
}
}
if(u == -1 || mx == oo) break;
vis[u] = 1;
for(int j = begin[u]; j; j = nxt[j]){//松弛
int v = to[j];
if(!vis[v] && dis[v] > (dis[u] + wht[j]))
dis[v] = dis[u] + wht[j];
}
}
}
int main(){
n = read<int>(); m = read<int>(); s = read<int>();
for(int i = 1; i <= n; i++) dis[i] = oo;
dis[s] = 0;
for(int i = 1; i <= m; i++){
int x, y, z;
x = read<int>(); y = read<int>(); z = read<int>();
add(x, y, z);
}
dijkstra(s);
for(int i = 1; i <= n; i++) printf("%d ", dis[i]);
return 0;
}
dijkstra(优先队列优化)//希望不要有人嘲讽我真的觉得这是仿spfa套dijk理论搞的
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
const int oo = 2147483647;
const int N = 10010;
const int M = 500010;
int n, m, s;
int dis[N], vis[N];
template <typename T>
T read(){
T N(0), F(1);
char C = getchar();
for(; !isdigit(C); C = getchar()) if(C == '-') F = -1;
for(; isdigit(C); C = getchar()) N = N*10 + C-48;
return N*F;
}
struct node{
int v, w;
node(){}
node(int a, int b){v = a; w = b;}
bool operator < (const node & a) const{
if(w == a.w) return v < a.v;
return w > a.w;
}
};
vector<node> E[N];
priority_queue<node> Q;
void dijkstra(int s){
for(int i = 1; i <= n; i++) dis[i] = oo;
dis[s] = 0;
Q.push(node(s, dis[s]));
while(!Q.empty()){
node x = Q.top(); Q.pop();
for(int i = 0; i < E[x.v].size(); i++){
node y = E[x.v][i];
if(dis[y.v] > x.w + y.w){
dis[y.v] = x.w + y.w;
Q.push(node(y.v, dis[y.v]));
}
}
}
}
int main(){
n = read<int>(); m = read<int>(); s = read<int>();
for(int i = 1; i <= n; i++) dis[i] = oo;
dis[s] = 0;
for(int i = 1; i <= m; i++){
int x, y, z;
x = read<int>(); y = read<int>(); z = read<int>();
E[x].pb(node(y, z));
// E[y].pb(node(x, z));
}
dijkstra(s);
for(int i = 1; i <= n; i++) printf("%d ", dis[i]);
return 0;
}
SPFA
#include<bits/stdc++.h>
using namespace std;
const int oo = 2147483647;
const int N = 10010;
const int M = 500010;
int n, m, s, e;
int dis[N], vis[N];
int to[M*2], nxt[M*2], wht[M*2], begin[N];
template <typename T>
T read(){
T N(0), F(1);
char C = getchar();
for(; !isdigit(C); C = getchar()) if(C == '-') F = -1;
for(; isdigit(C); C = getchar()) N = N*10 + C-48;
return N*F;
}
void add(int x, int y, int z){
to[++e] = y; nxt[e] = begin[x]; wht[e] = z; begin[x] = e;
// to[++e] = x; nxt[e] = begin[y]; wht[e] = z; begin[y] = e;
}
void init(){
for(int i = 1; i <= n; i++) dis[i] = oo, vis[i] = 1;
dis[s] = vis[s] = 0;
}
queue<int> Q;
void spfa(){
Q.push(s);
while(!Q.empty()){
int u = Q.front();
Q.pop(); vis[u] = 1;
for(int i = begin[u]; i; i = nxt[i]){
int v = to[i], w = wht[i];
if(dis[u] + w < dis[v]){
dis[v] = dis[u] + w;
if(vis[v]){
Q.push(v);
vis[v] = 0;
}
}
}
}
}
int main(){
n = read<int>(); m = read<int>(); s = read<int>();
for(int i = 1; i <= m; i++){
int x, y, z;
x = read<int>(); y = read<int>(); z = read<int>();
add(x, y, z);
}
init();
spfa();
for(int i = 1; i <= n; i++) printf("%d ", dis[i]);
return 0;
}
Bellman_Ford
[没写过,暂时贴一个别人的,如侵权即删!]
实现简单,复杂度 \(O(|V|*|E|)\)
bool Bellman_Ford(s){
memset(d,INF,sizeof(INF));
for(int i=0 ; i<nv ; ++i){
for(int j=0 ; j<E.size() ; ++j)
d[E[i].to] = min(d[E[i].to],d[E[i].from]+E[i].weight);
}
//负环判定
for(int i=0 ; i<E.size() ; ++i){
if(d[E[i].to]<d[E[i].from]+E[i].weight)return false;
}
return true;
}
次短路
正向spfa+反向spfa然后每次提出一条边判断次短路。
#include<bits/stdc++.h>
using namespace std;
const int oo = 2147483647;
const int N = 5005;
const int M = 100005;
template <typename T>
T read(){
T n(0), f(1);
char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
for(; isdigit(ch); ch = getchar()) n = n*10 + ch-48;
return n*f;
}
int n, m, e, ans, tmp;
int begin[N], dl[N], dr[N], vis[N];
struct node{
int to, w, nxt;
}E[M>>1];
void add(int x, int y, int z){
E[++e].to = y; E[e].w = z; E[e].nxt = begin[x]; begin[x] = e;
E[++e].to = x; E[e].w = z; E[e].nxt = begin[y]; begin[y] = e;
}
void spfa(int s, int d[]){
for(int i = 1; i <= n; i++) d[i] = oo, vis[i] = 0;
vis[s] = 1; d[s] = 0;
queue<int> Q;
Q.push(s);
while(!Q.empty()){
int u = Q.front(); Q.pop();
vis[u] = 0;
for(int i = begin[u]; i; i = E[i].nxt){
int v = E[i].to;
if(!vis[v]){
if(d[v] > d[u]+E[i].w){
d[v] = d[u] + E[i].w;
vis[v] = 1;
Q.push(v);
}
}
}
}
}
int main(){
n = read<int>(); m = read<int>();
for(int i = 1; i <= m; i++){
int x, y, z;
x = read<int>(); y = read<int>(); z = read<int>();
add(x, y, z);
}
spfa(1, dl);
spfa(n, dr);
ans = oo;
for(int u = 1; u <= n; u++){
for(int i = begin[u]; i; i = E[i].nxt){
int v = E[i].to;
int w_ = E[i].w;
tmp = dl[u] + dr[v] + w_;
if(tmp > dl[n] && ans > tmp) ans = tmp;
}
}
printf("%d\n", ans);
return 0;
}
多源最短路径(点对之间的最短路)
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cstring>
#define MAXN (100+10)
#define INF 0x3f3f3f3f
int edge[MAXN][MAXN];
int n;
int main(){
while(scanf("%d", &n) && n){
int ans = INF, num;
memset(edge, INF, sizeof(edge));
for(int i = 1; i <= n; i++){
int t;
scanf("%d", &t);
while(t--){
int j, w;
scanf("%d%d", &j, &w);
edge[i][j] = w;
}
}
for(int k = 1; k <= n; k++)
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
if(edge[i][k] + edge[k][j] < edge[i][j]) edge[i][j] = edge[i][k]+edge[k][j];
for(int i = 1; i <= n; i++){
int tmp = 0;
for(int j = 1; j <= n; j++){
if(i == j) continue;
if(edge[i][j] > tmp){
tmp = edge[i][j];
}
}
if(ans > tmp){
ans = tmp, num = i;
}
}
printf("%d %d\n", num, ans);
}
return 0;
}
最小生成树
#include<bits/stdc++.h>
using namespace std;
const int N = 5010;
const int M = 200010;
int n, m, ans;
int r[N], f[N];
struct node{
int u, v, w;
}E[M];
template <typename T>
T read(){
T N(0), F(1);
char C = getchar();
for(; !isdigit(C); C = getchar()) if(C == '-') F = -1;
for(; isdigit(C); C = getchar()) N = N*10 + C-48;
return N*F;
}
bool cmp(node x, node y){
return x.w < y.w;
}
void init(){
for(int i = 1; i <= n; i++) f[i] = i;
memset(r, 0, sizeof(r));
}
int find(int x){
return x == f[x] ? x : f[x] = find(f[x]);
}
void mix(int a, int b){
int fa = find(f[a]);
int fb = find(f[b]);
if(fa == fb) return;
if(r[fa] < r[fb]){
f[fa] = fb;
}
else{
f[fb] = fa;
if(r[fa] == r[fb]) r[fa]++;
}
}
int krus(){
int em = 0;
sort(E+1, E+m+1, cmp);
for(int i = 1; i <= m && em != n-1; i++){
int fu = find(E[i].u);
int fv = find(E[i].v);
if(find(E[i].u) != find(E[i].v)){
mix(E[i].u, E[i].v);
ans += E[i].w;
em++;
}
}
if(em < n-1) ans = -1;
return ans;
}
int main(){
n = read<int>(); m = read<int>();
for(int i = 1; i <= m; i++){
E[i].u = read<int>();
E[i].v = read<int>();
E[i].w = read<int>();
}
init();
printf("%d\n", krus());
return 0;
}
有向图Tarjan求scc个数
hdu1269 scc个数为1输出Yes,否则No.
#include<bits/stdc++.h>
using namespace std;
const int N = 10010;
template <typename T>
T read(){
T n(0), f(1);
char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
for(; isdigit(ch); ch = getchar()) n = n*10 + ch-48;
return n*f;
}
int n, m, cnt, tot, top;
int dfn[N], low[N], st[N], vis[N];
vector<int> E[N];
void chkmn(int &a, int b){
a = a < b ? a : b;
}
void init(){
cnt = top = tot = 0;
for(int i = 1; i <= n; ++i){
dfn[i] = -1;
vis[i] = 0;
}
for(int i = 1; i <= n; ++i) E[i].clear();
}
void Tarjan(int u){
vis[u] = 1;
dfn[u] = low[u] = ++tot;
st[++top] = u;
for(int i = 0; i < E[u].size(); ++i){
int v = E[u][i];
if(dfn[v] == -1){
Tarjan(v);
chkmn(low[u], low[v]);
}
else if(vis[v]) chkmn(low[u], dfn[v]);
}
int v_;
if(dfn[u] == low[u]){
++cnt;
do{
v_ = st[top--];
vis[v_] = 0;
}while(u != v_);
}
}
int main(){
while(scanf("%d%d", &n, &m)!=EOF){
if(!n && !m) break;
init();
for(int i = 1; i <= m; ++i){
int x, y;
x = read<int>(); y = read<int>();
E[x].push_back(y);
}
for(int i = 1; i <= n; ++i)
if(dfn[i] == -1) Tarjan(i);
if(cnt == 1) puts("Yes");
else puts("No");
}
return 0;
}
有向图Tarjan求scc内最小权值和
hdu1827 RT
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
const int N = 2010;
template <typename T>
T read(){
T n(0), f(1);
char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
for(; isdigit(ch); ch = getchar()) n = n*10 + ch-48;
return n*f;
}
int n, m, cnt, tot, top;
int dfn[N], low[N], st[N], vis[N], sccw[N], deg[N], f[N], w[N];
vector<int> E[N], G[N];
void chkmn(int &a, int b){
a = a < b ? a : b;
}
void init(){
cnt = top = tot = 0;
for(int i = 0; i <= n; ++i){
dfn[i] = -1;
vis[i] = f[i] = 0;
deg[i] = 0;
sccw[i] = 2147483647;
}
for(int i = 0; i <= n; ++i) E[i].clear(), G[i].clear();
}
void Tarjan(int u){
vis[u] = 1;
dfn[u] = low[u] = ++tot;
st[++top] = u;
for(int i = 0; i < E[u].size(); ++i){
int v = E[u][i];
if(dfn[v] == -1){
Tarjan(v);
chkmn(low[u], low[v]);
}
else if(vis[v]) chkmn(low[u], dfn[v]);
}
int v_;
if(dfn[u] == low[u]){
++cnt; sccw[cnt] = w[u];
do{
v_ = st[top--];
vis[v_] = 0;
f[v_] = cnt;
chkmn(sccw[cnt], w[v_]);
}while(u != v_);
}
}
int main(){
while(scanf("%d%d", &n, &m)==2){
init();
for(int i = 1; i <= n; ++i) w[i] = read<int>();
for(int i = 1; i <= m; ++i){
int x, y;
x = read<int>(); y = read<int>();
E[x].push_back(y);
}
for(int i = 1; i <= n; ++i)
if(dfn[i] == -1) Tarjan(i);
for(int i = 1; i <= n; ++i){
for(int j = 0; j < E[i].size(); ++j){
if(f[i] != f[E[i][j]]){
G[f[i]].push_back(f[E[i][j]]);
deg[f[E[i][j]]]++;
}
}
}
int ans1 = 0, ans2 = 0;
for(int i = 1; i <= cnt; ++i){
if(!deg[i]){
ans1++;
ans2 += sccw[i];
}
}
printf("%d %d\n", ans1, ans2);
}
return 0;
}
有向图Tarjan缩点求路径点权和最大
模板P3387 缩点+DP(我写的spfa跑路径而非DP计算)
#include<bits/stdc++.h>
using namespace std;
const int N = 10010;
const int M = 100010;
template<typename T>
T read(){
T n(0), f(1);
char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
for(; isdigit(ch); ch = getchar()) n = n*10 + ch-48;
return n*f;
}
#define read() read<int>()
#define pb push_back
int n, m, cnt, top, tot;
int dfn[N], val[N], low[N], sccw[N], f[N], vis[N], st[N];
vector<int> E[N];
void chkmx(int& a, int b){
a = a > b ? a : b;
}
void chkmn(int& a, int b){
a = a < b ? a : b;
}
void Tarjan(int u){
dfn[u] = low[u] = ++tot;
vis[u] = 1; st[++top] = u;
for(int i = 0; i < E[u].size(); ++i){
int v = E[u][i];
if(dfn[v] == -1){
Tarjan(v);
chkmn(low[u], low[v]);
}
else if(vis[v]) chkmn(low[u], dfn[v]);
}
int v_ = 0;
if(low[u] == dfn[u]){
++cnt;
do{
v_ = st[top--];
vis[v_] = 0;
f[v_] = cnt;
sccw[cnt] += val[v_];
}while(u != v_);
}
}
int e, ans;
int dis[N], bl[N], to[M<<1], nxt[M<<1], Begin[N];
void addedge(int x, int y){
to[++e] = y; nxt[e] = Begin[x]; Begin[x] = e;
}
void spfa(int s){
queue<int> Q;
Q.push(s); bl[s] = 1; dis[s] = sccw[s];
while(!Q.empty()){
int u = Q.front(); Q.pop();
bl[u] = 0;
for(int i = Begin[u]; i; i = nxt[i]){
int v = to[i];
chkmx(dis[v], dis[u]+sccw[v]);
if(!bl[v]){
Q.push(v);
bl[v] = 1;
}
}
}
for(int i = 1; i <= cnt; ++i) chkmx(ans, dis[i]);
}
int deg[N];
int main(){
n = read(); m = read();
for(int i = 1; i <= n; ++i) val[i] = read(), dfn[i] = -1;
for(int i = 1; i <= m; ++i){
int x, y;
x = read();
y = read();
E[x].pb(y);
}
for(int i = 1; i <= n; ++i)
if(dfn[i] == -1) Tarjan(i);
for(int i = 1; i <= n; ++i){
for(int j = 0; j < E[i].size(); ++j){
if(f[i] != f[E[i][j]]){
addedge(f[i], f[E[i][j]]);
++deg[f[E[i][j]]];
}
}
}
for(int i = 1; i <= cnt; ++i){
if(!deg[i]) spfa(i);
}
printf("%d\n", ans);
return 0;
}
无向图Tarjan求割点和桥
[割点] 根节点子树>=2则为割点; 非根节点u的子节点v中,low[v]>=dfn[u]则为割点。
[桥] 节点u的子节点v中,low[v] > dfn[u]则(u, v)为桥。
板子:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
template<typename T> T read(){
T n(0), f(1);
char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
for(; isdigit(ch); ch = getchar()) n = n*10 + ch-48;
return n*f;
}
int n, m, cnt;
int vis[N], dfn[N], low[N], fa[N];
vector<int> G[N];
void init(){
for(int i = 1; i <= n; ++i){
dfn[i] = low[i] = 0;
fa[i] = 0;
vis[i] = false;
}
}
void Tarjan(int u, int f = 0){
fa[u] = f;
dfn[u] = low[u] = ++cnt;
for(int i = 0; i < G[u].size(); ++i){
int v = G[u][i];
if(!dfn[v]){
Tarjan(v, u);
low[u] = min(low[u], low[v]);
}
else if(v != f)
low[u] = min(low[u], dfn[v]);
}
}
int main(){
n = read<int>(); m = read<int>();
for(int i = 1; i <= m; ++i){
int x, y;
x = read<int>();
y = read<int>();
G[x].push_back(y);
G[y].push_back(x);
}
init();
Tarjan(1);
for(int i = 2; i <= n; ++i){
int f = fa[i];
if(dfn[f] <= low[i]) vis[f] = true;
}
if(G[1].size() > 1) vis[1] = true;
for(int i = 1; i <= n; ++i) if(vis[i]) printf("%d\n", i);//割点
for(int i = 1; i <= n; ++i){
int f = fa[i];
if(f > 0 && dfn[f] < low[i]) printf("%d, %d\n", f, i);//桥
}
return 0;
}
网络流
增广路算法求最大流
模板LG P3376
DFS太慢,EK算法其实也很慢....(既然是NOIP选手不掌握fastest也没问题吧)
#include<bits/stdc++.h>
using namespace std;
const int oo = 0x7f7f7f7f;
const int N = 10010;
template <typename T>
T read(){
T n(0), f(1);
char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
for(; isdigit(ch); ch = getchar()) n = n*10 + ch-48;
return n*f;
}
struct Edge{
int u, v, cap, flow;
Edge(int from, int to, int c, int f):u(from), v(to), cap(c), flow(f){}
};
int n, m;
int a[N], p[N];
vector<Edge> edges;
vector<int> G[N];
#define pb push_back
void init(){
for(int i = 1; i <= n; ++i) G[i].clear();
edges.clear();
}
void addedge(int u, int v, int cap){
edges.pb(Edge(u, v, cap, 0));
edges.pb(Edge(v, u, 0, 0));
int tm = edges.size();
G[u].pb(tm-2);
G[v].pb(tm-1);
}
int EK(int s, int t){
int flow = 0;
for(;;){
memset(a, 0, sizeof(a));
queue<int> Q;
Q.push(s);
a[s] = oo;
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.v] && e.cap > e.flow){
p[e.v] = G[x][i];
a[e.v] = min(a[x], e.cap-e.flow);
Q.push(e.v);
}
}
if(a[t]) break;
}
if(!a[t]) break;
for(int i = t; i != s; i = edges[p[i]].u){
edges[p[i]].flow += a[t];
edges[p[i]^1].flow -= a[t];
}
flow += a[t];
}
return flow;
}
int main(){
int s, t;
n = read<int>(); m = read<int>();
s = read<int>(); t = read<int>();
init();
for(int i = 1; i <= m; ++i){
int u, v, w;
u = read<int>();
v = read<int>();
w = read<int>();
addedge(u, v, w);
}
printf("%d\n", EK(s, t));
return 0;
}
最小费用最大流
模板LG P3381
用Bellmanford求
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5010;
const int M = 50010;
const int oo = 0x7f7f7f7f;
template <typename T>
T read(){
T n(0), f(1);
char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
for(; isdigit(ch); ch = getchar()) n = n*10 + ch-48;
return n*f;
}
#define read() read<int>()
struct Edge{
int from, to, cap, flow, cost;
Edge(int u, int v, int c, int f, int val):from(u), to(v), cap(c), flow(f), cost(val){}
};
int n, m;
vector<Edge> edges;
vector<int> G[N];
int a[N], d[N], p[N], inq[N];
#define pb push_back
void addedge(int x, int y, int w, int v){
edges.pb(Edge(x, y, w, 0, v));
edges.pb(Edge(y, x, 0, 0, -v));
int tm = edges.size();
G[x].pb(tm-2);
G[y].pb(tm-1);
}
void init(){
for(int i = 1; i <= n; ++i) G[i].clear();
edges.clear();
}
bool BMFD(int s, int t, int& flow, ll& cost){
memset(inq, 0, sizeof(inq));
for(int i = 1; i <= n; ++i) d[i] = oo;
d[s] = p[s] = 0; a[s] = oo; inq[s] = 1;
queue<int> Q;
Q.push(s);
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;
a[e.to] = min(a[u], e.cap-e.flow);
p[e.to] = G[u][i];
if(!inq[e.to]){ Q.push(e.to); inq[e.to] = 1; }
}
}
}
if(d[t] == oo) return false;
flow += a[t];
cost += 1ll*d[t]*a[t];
for(int i = t; i != s; i = edges[p[i]].from){
edges[p[i]].flow += a[t];
edges[p[i]^1].flow -= a[t];
}
return true;
}
int MCMF(int s, int t, ll& cost){
int flow = 0; cost = 0;
while(BMFD(s, t, flow, cost));
return flow;
}
int main(){
int s, t;
n = read(); m = read();
s = read(); t = read();
init();
for(int i = 1; i <= m; ++i){
int x, y, w, v;
x = read(); y = read();
w = read(); v = read();
addedge(x, y, w, v);
}
ll cost = 0;
printf("%d ", MCMF(s, t, cost));
printf("%lld\n", cost);
return 0;
}