#include <iostream>
#include <stdio.h>
#include <utility>
#include <vector>
#include <deque>
using namespace std;
const int INF = 1e8;
struct pack {
int nd, cst;
};
struct flm {
int cap, cst;
};
int cnt[1000][1000];
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);
}
}
void pop() {
pos[arr[heapSize].nd] = 1;
swap(arr[1], arr[heapSize--]);
sift(1);
}
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;
int maxflow;
int *minDist;
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 -> minDist = new int[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;
}
}
}
void bellmanFord() {
deque<int> q;
bool *inQ = new bool[n + 1];
for (int i = 1; i <= n; i++) {
minDist[i] = INF;
inQ[i] = 0;
}
minDist[src] = 0;
inQ[src] = 1;
q.push_back(src);
int c = 0;
while (!q.empty()) {
c++;
int curr = q.front();
q.pop_front();
inQ[curr] = 0;
int len = edges[curr].size();
for (int i = 0; i < len; i++) {
int nxt = edges[curr][i];
if (ntwrk[curr][nxt].cap) {
if (minDist[curr] + ntwrk[curr][nxt].cst < minDist[nxt]) {
cnt[curr][nxt]++;
minDist[nxt] = minDist[curr] + ntwrk[curr][nxt].cst;
if (!inQ[nxt]) {
inQ[nxt] = 1;
q.push_back(nxt);
}
}
}
}
}
}
inline void preprocess() {
bellmanFord();
for (int nd = 1; nd <= n; nd++) {
int len = edges[nd].size();
for (int i = 0; i < len; i++) {
int nxt = edges[nd][i];
ntwrk[nd][nxt].cst = minDist[nd] + ntwrk[nd][nxt].cst - minDist[nxt];
}
}
}
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) {
if (flow[curr.nd][dest] != ntwrk[curr.nd][dest].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;
flow[son][father[son]] -= minFlow;
cstPth += minFlow * ntwrk[father[son]][son].cst;
son = father[son];
}
cstPth -= minDist[dest] * minFlow;
maxflow += minFlow;
return cstPth;
}
int solveNetwork() {
maxflow = 0;
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, {0, -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;
}