/*
Dublam nodurile si aplicam algoritmul de cuplaj ce foloseste flux. Graful va arata
in felul urmator: Nodul de start (0) este legat de nodul X (X <- 1..N) cu capacitatea
gradului de iesire al lui X (deoarece aceste este numarul de muchii pe care X le primeste
ca sa le "dea mai departe"); X este legat de toate dublurile nodurilor, cu exceptia
dublurii lui, adica toate nodurile Y <- 1..N, X != Y, printr-o muchie de cost 1, deoarece
X poate trimite in Y o singura muchie; Din fiecare dublura avem muchie catre nodul final de
capacitate egala cu gradul de intrare al nodului respectiv (deoarece atatea muchii au fost duse
SPRE el).
Rulam algoritmul de determinare a fluxului maxim si rezultatul va fi cuplajul cautat, adica
numarul de muchii pe care le-am trasat (egal de asemenea cu jumatate din suma tuturor gradelor).
Vom considera nodurile initiale (1..N) si sursa (0) ca fiind in primul set, iar dublurile
si destinatia in al doilea.
Pentru a obtine muchiile ce alcatuiesc cuplajul, vom afisa muchiile care leaga un nod
din primul set cu un nod din al doilea set si au capacitate 0 (adica le-am utilizat).
Pentru reprezentarea grafului, am indexat nodurile astfel:
-> nod start = 0
-> nodurile: 1 .. n (primul set)
-> dublurile: n + 1 .. 2 * n (al doilea set)
-> nod final = 2 * n + 1
*/
#include <bits/stdc++.h>
using namespace std;
std::vector<int> bellman_ford (std::vector<std::vector<int>> &nodes_list,
std::vector<std::vector<int>> &capacity,
std::vector<std::vector<int>> &price) {
int n = nodes_list.size();
std::vector<int> distances(n + 1, 1e9);
std::queue<int> updated;
std::vector<bool> in_queue(n + 1, false);
distances[0] = 0;
updated.push(0);
in_queue[0] = true;
while (!updated.empty()) {
int current = updated.front();
updated.pop();
in_queue[current] = false;
for (auto nbh: nodes_list[current]) {
if (capacity[current][nbh]) {
if (distances[current] + price[current][nbh] < distances[nbh]) {
distances[nbh] = distances[current] + price[current][nbh];
if (!in_queue[nbh]) {
updated.push(nbh);
in_queue[nbh] = true;
}
}
}
}
}
return distances;
}
int dijkstra (std::vector<std::vector<int>> &nodes_list,
std::vector<std::vector<int>> &capacity,
std::vector<std::vector<int>> &price,
std::vector<int> &initial_distances,
std::vector<int> &positive_distances,
int start, int finish,
int &flow, int &flow_price) {
int n = nodes_list.size();
std::vector<int> dist(n, 1e9);
std::vector<int> father(n, 0);
priority_queue<int, vector<int>, greater<>> heap_dist;
dist[start] = positive_distances[start] = 0;
heap_dist.push(start);
while (!heap_dist.empty()) {
auto current_node = heap_dist.top();
heap_dist.pop();
for (auto nbh : nodes_list[current_node]) {
if (capacity[current_node][nbh]) {
int new_dist_to_nbh = dist[current_node] + price[current_node][nbh] + initial_distances[current_node] - initial_distances[nbh];
if (new_dist_to_nbh < dist[nbh]) {
dist[nbh] = new_dist_to_nbh;
father[nbh] = current_node;
positive_distances[nbh] = positive_distances[current_node] + price[current_node][nbh];
heap_dist.push(nbh);
}
}
}
}
if (dist[finish] == 1e9) return false;
int path_capacity = 1e9, path_price = 0;
int current = finish;
while (current != start) {
path_capacity = min(path_capacity, capacity[father[current]][current]);
path_price += price[father[current]][current];
current = father[current];
}
flow += path_capacity;
flow_price += path_capacity * positive_distances[finish];
current = finish;
while (current != start) {
capacity[current][father[current]] += path_capacity;
capacity[father[current]][current] -= path_capacity;
current = father[current];
}
return true;
}
int main()
{
std::ifstream fin("fmcm.in");
std::ofstream fout("fmcm.out");
int n, m, start, finish;
fin >> n >> m >> start >> finish;
std::vector<std::vector<int>> adj_list(2 * (n + 1));
std::vector<std::vector<int>> capacity(2 * (n + 1), std::vector<int>(2 * (n + 1), 0));
std::vector<std::vector<int>> price(2 * (n + 1), std::vector<int>(2 * (n + 1), 0));
for (int i = 1; i <= m; ++i) {
int source, dest, cap, cost;
fin >> source >> dest >> cap >> cost;
adj_list[source].push_back(dest);
adj_list[dest].push_back(source);
capacity[source][dest] = cap;
price[source][dest] = cost;
price[dest][source] = -cost;
}
auto initial_distances = bellman_ford(adj_list, capacity, price);
std::vector<int> positive_distances(n + 1, 1e9);
int flow = 0, flow_price = 0;
while (dijkstra(adj_list, capacity, price, initial_distances, positive_distances, start, finish, flow, flow_price));
fout << flow_price;
return 0;
}