#include <iostream>
#include <stdio.h>
#include <utility>
#include <vector>
using namespace std;
const int INF = 1e8;
struct pack {
int nd, cst;
};
struct flm {
int cap, cst;
};
class Heap {
private:
int heapSize;
public:
int maxHeapSize;
pack *arr;
int *pos;
Heap(int maxHeapSize) {
this -> maxHeapSize = maxHeapSize;
this -> heapSize = 0;
this -> arr = new pack[maxHeapSize + 1];
this -> pos = new int[maxHeapSize + 1];
}
inline int lSon(int node) {
return node << 1;
}
inline int rSon(int node) {
return (node << 1) + 1;
}
inline int father(int node) {
return node >> 1;
}
inline void sift(int node) {
int son;
do {
son = 0;
if (lSon(node) <= heapSize && arr[lSon(node)].cst < arr[node].cst)
son = lSon(node);
if (rSon(node) <= heapSize && arr[rSon(node)].cst < arr[son].cst)
son = rSon(node);
if (son) {
pos[arr[node].nd] = son;
pos[arr[son].nd] = node;
swap(arr[node], arr[son]);
node = son;
}
}while (son);
}
inline void percolate(int node) {
while (father(node) && arr[father(node)].cst > arr[node].cst) {
pos[arr[father(node)].nd] = node;
pos[arr[node].nd] = father(node);
swap(arr[father(node)], arr[node]);
node = father(node);
}
}
inline void pop() {
pos[arr[heapSize].nd] = 1;
swap(arr[1], arr[heapSize--]);
sift(1);
}
inline void inserthp(pack newE) {
arr[++heapSize] = newE;
pos[newE.nd] = heapSize;
percolate(heapSize);
}
inline int getSize() {
return heapSize;
}
inline pack top() {
return arr[1];
}
inline int getPos(int nd) {
return pos[nd];
}
inline bool isempty() {
if (heapSize)
return 0;
return 1;
}
void change (int pos, int val) {
arr[pos].cst = val;
percolate(pos);
}
};
class minCostFlow {
public:
int n;
vector<int> *edges;
int *father;
int **flow;
flm **ntwrk;
bool *vis;
int src, dest;
int minCost;
minCostFlow(int n, vector<pair<int, flm>> *edges, int src, int dest) {
this -> n = n;
this -> edges = new vector<int>[n + 1];
this -> father = new int[n + 1];
this -> flow = new int*[n + 1];
this -> ntwrk = new flm*[n + 1];
this -> vis = new bool[n + 1];
this -> src = src;
this -> dest = dest;
for (int i = 1; i <= n; i++) {
this -> flow[i] = new int[n + 1];
this -> ntwrk[i] = new flm[n + 1];
for (int j = 0; j <= n; j++) {
this -> flow[i][j] = 0;
this -> ntwrk[i][j] = {0, 0};
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++)
this -> flow[i][j] = 0;
int len = edges[i].size();
for (int j = 0; j < len; j++) {
pair<int, flm> pr = edges[i][j];
this -> edges[i].push_back(pr.first);
this -> ntwrk[i][pr.first] = pr.second;
this -> ntwrk[pr.first][i] = {-pr.second.cap, -pr.second.cst};
}
}
}
inline void preprocess() {
minCost = INF;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
minCost = min(minCost, ntwrk[i][j].cst);
if (minCost < 0)
minCost = -minCost;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
ntwrk[i][j].cst += minCost;
}
inline bool dijkstra() {
for (int i = 0; i <= n; i++)
vis[i] = 0;
Heap myHeap(n);
myHeap.inserthp({src, 0});
father[src] = 0;
int minPth = INF;
vis[src] = 1;
while (!myHeap.isempty()) {
pack curr = myHeap.top();
int len = edges[curr.nd].size();
for (int i = 0; i < len; i++) {
pack nxt = {edges[curr.nd][i], ntwrk[curr.nd][edges[curr.nd][i]].cst};
if (nxt.nd == dest && flow[curr.nd][nxt.nd] != ntwrk[curr.nd][nxt.nd].cap && minPth > curr.cst + nxt.cst) {
vis[dest] = 1;
father[dest] = curr.nd;
minPth = curr.cst + nxt.cst;
continue;
}
if (flow[curr.nd][nxt.nd] != ntwrk[curr.nd][nxt.nd].cap) {
if (!vis[nxt.nd]) {
myHeap.inserthp({nxt.nd, curr.cst + nxt.cst});
father[nxt.nd] = curr.nd;
vis[nxt.nd] = 1;
}
else {
int pos = myHeap.getPos(nxt.nd);
if (myHeap.arr[pos].cst > curr.cst + nxt.cst) {
father[nxt.nd] = curr.nd;
myHeap.change(pos, curr.cst + nxt.cst);
}
}
}
}
myHeap.pop();
}
return vis[dest];
}
inline int pushFlow() {
int son = dest;
int minFlow = INF, cstPth;
while (father[son]) {
minFlow = min(ntwrk[father[son]][son].cap - flow[father[son]][son], minFlow);
son = father[son];
}
cstPth = 0;
son = dest;
while (father[son]) {
flow[father[son]][son] += minFlow;
cstPth += minFlow * ntwrk[father[son]][son].cst - minFlow * minCost;
son = father[son];
}
return cstPth;
}
inline int solveNetwork() {
int cstFin = 0;
while (dijkstra())
cstFin += pushFlow();
return cstFin;
}
};
int main()
{
vector<pair<int, flm>> *edges;
int n, m, src, dest, v1, v2, cst, cap;
FILE *fin = fopen("fmcm.in", "r");
fscanf(fin, "%d%d%d%d", &n, &m, &src, &dest);
edges = new vector<pair<int, flm>>[n + 1];
for (int i = 0; i < m; i++) {
fscanf(fin, "%d%d%d%d", &v1, &v2, &cap, &cst);
edges[v1].push_back({v2, {cap, cst}});
edges[v2].push_back({v1, {-cap, -cst}});
}
fclose(fin);
FILE *fout = fopen("fmcm.out", "w");
minCostFlow slv(n, edges, src, dest);
slv.preprocess();
fprintf(fout, "%d", slv.solveNetwork());
fclose(fout);
return 0;
}