#include <bits/stdc++.h>
using namespace std;
///---------------------------------------------------
const int BUFFER_SIZE = (1 << 16);
char buffer[BUFFER_SIZE];
int position = BUFFER_SIZE;
inline char getChar()
{
if ( position == BUFFER_SIZE )
{
position = 0;
fread(buffer, BUFFER_SIZE, 1, stdin);
}
return buffer[position++];
}
inline int getInt()
{
register int a = 0;
char ch;
int sgn = 1;
do
{
ch = getChar();
} while ( !isdigit(ch) && ch != '-' );
if ( ch == '-' )
{
sgn = -1;
ch = getChar();
}
do
{
a = (a << 3) + (a << 1) + ch - '0';
ch = getChar();
} while ( isdigit(ch) );
return a * sgn;
}
///---------------------------------------------------
class MinCostMaxFlow
{
private:
using Pair = pair<int,int>;
static const int NIL;
static const int INF;
class Edge
{
public:
int flow;
int capacity;
int cost;
int node;
int next;
Edge(int flow, int capacity, int cost, int node, int next)
{
this->flow = flow;
this->capacity = capacity;
this->cost = cost;
this->node = node;
this->next = next;
}
};
int N;
vector<Edge> graph;
vector<int> head, father, pointer;
vector<int> realDist, oldDist, newDist;
void bellmanFord(int S)
{
vector<bool> inQueue(N, false);
fill(oldDist.begin(), oldDist.end(), INF);
queue<int> Q;
Q.push(S);
inQueue[S] = true;
oldDist[S] = 0;
while (!Q.empty())
{
int node = Q.front();
Q.pop();
inQueue[node] = false;
for (int p = head[node]; p != NIL; p = graph[p].next)
{
Edge e = graph[p];
int son = e.node;
if (e.capacity > e.flow && oldDist[son] > oldDist[node] + e.cost)
{
oldDist[son] = oldDist[node] + e.cost;
if (!inQueue[son])
{
inQueue[son] = true;
Q.push(son);
}
}
}
}
}
bool dijkstra(int S, int T)
{
fill(realDist.begin(), realDist.end(), INF);
fill(newDist.begin(), newDist.end(), INF);
fill(father.begin(), father.end(), NIL);
fill(pointer.begin(), pointer.end(), NIL);
priority_queue<Pair,vector<Pair>,greater<Pair>> minHeap;
minHeap.push({0, S});
realDist[S] = newDist[S] = 0;
father[S] = S;
while (!minHeap.empty())
{
auto pii = minHeap.top();
minHeap.pop();
int node = pii.second;
int d = pii.first;
if (newDist[node] != d)
continue;
for (int p = head[node]; p != NIL; p = graph[p].next)
{
Edge e = graph[p];
int son = e.node;
int new_distance = oldDist[node] - oldDist[son] + e.cost + newDist[node];
if (e.capacity > e.flow && newDist[son] > new_distance)
{
newDist[son] = new_distance;
realDist[son] = realDist[node] + e.cost;
father[son] = node;
pointer[son] = p;
minHeap.push({newDist[son], son});
}
}
}
for (int i = 0; i < N; ++i)
oldDist[i] = realDist[i];
return realDist[T] != INF;
}
public:
MinCostMaxFlow(int _N) : N(_N), graph(), head(), father(), pointer(),
realDist(), oldDist(), newDist() {
head.resize(N, NIL);
father.resize(N);
pointer.resize(N);
realDist.resize(N);
oldDist.resize(N);
newDist.resize(N);
}
void addEdge(int from, int to, int capacity, int cost)
{
from--; to--;
graph.push_back(Edge(0, capacity, cost, to, head[from]));
head[from] = graph.size() - 1;
}
void addDoubleEdge(int x, int y, int capacity, int cost)
{
addEdge(x, y, capacity, cost);
addEdge(y, x, 0, -cost);
}
pair<int,int> minCostMaxFlow(int S, int T)
{
S--; T--;
int totalFlow = 0;
int costTotalFlow = 0;
bellmanFord(S);
while (dijkstra(S, T))
{
int fmin = INF;
int node = T;
while (node != S)
{
fmin = min(fmin, graph[pointer[node]].capacity - graph[pointer[node]].flow);
node = father[node];
}
node = T;
while (node != S)
{
graph[pointer[node]].flow += fmin;
graph[pointer[node] ^ 1].flow -= fmin;
node = father[node];
}
totalFlow += fmin;
costTotalFlow += fmin * realDist[T];
}
return make_pair(totalFlow, costTotalFlow);
}
};
const int MinCostMaxFlow::NIL = -1;
const int MinCostMaxFlow::INF = numeric_limits<int>::max();
int main()
{
freopen("fmcm.in", "r", stdin);
freopen("fmcm.out", "w", stdout);
int N, M, S, T;
N = getInt();
M = getInt();
S = getInt();
T = getInt();
MinCostMaxFlow MCMF(N);
for (int i = 0; i < M; ++i)
{
int x, y, cap, cst;
x = getInt();
y = getInt();
cap = getInt();
cst = getInt();
MCMF.addDoubleEdge(x, y, cap, cst);
}
printf("%d\n", MCMF.minCostMaxFlow(S, T).second);
return 0;
}