#include <fstream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <utility>
#include <queue>
using namespace std;
template <int NMAX, int MMAX>
class MaxFlowMinCost {
public:
MaxFlowMinCost() { m = 0; }
inline void setN(int _n) { n = _n; }
inline void setS(int _s) { s = _s; }
inline void setT(int _t) { t = _t; }
inline int getN() { return n; }
inline int getS() { return s; }
inline int getT() { return t; }
void clear() {
m = 0;
for (int i = 1; i <= n; ++ i)
graph[i].clear();
}
void reset() {
for (int i = 0; i < m; ++ i)
edges[i].flow = 0;
}
void addEdge(int from, int to, int cap, int cost) {
edges[m ++] = Edge(from, to, 0, cap, cost);
edges[m ++] = Edge(to, from, 0, 0, -cost);
graph[from].push_back(m - 2);
graph[to].push_back(m - 1);
}
inline pair <int, int> computeFlow() {
return JohnsonDinic();
}
private:
struct Edge {
int from, to;
int flow, cap, cost;
Edge(int _from = 0, int _to = 0, int _flow = 0, int _cap = 0, int _cost = 0):
from(_from), to(_to), flow(_flow), cap(_cap), cost(_cost) {}
inline int other(int node) {
return from ^ to ^ node;
}
};
int n, m, s, t;
vector <int> graph[NMAX];
Edge edges[2 * MMAX];
const int INF = 1E9;
int potentials[NMAX];
queue <int> q;
bool inQueue[NMAX];
void Bellman() {
for (int i = 1; i <= n; ++ i) {
inQueue[i] = false;
potentials[i] = INF;
}
inQueue[s] = true;
potentials[s] = 0;
q.push(s);
while (!q.empty()) {
int node = q.front();
q.pop();
inQueue[node] = false;
for (auto edge: graph[node]) {
int other = edges[edge].other(node);
int cost = edges[edge].cost;
if (edges[edge].flow < edges[edge].cap && potentials[node] + cost < potentials[other]) {
potentials[other] = potentials[node] + cost;
if (!inQueue[other]) {
inQueue[other] = true;
q.push(other);
}
}
}
}
}
int dist[NMAX];
int father[NMAX];
bool vis[NMAX];
priority_queue <pair <int, int> > _queue;
bool Dijkstra() {
memset(vis, 0, (n + 1) * sizeof(bool));
for (int i = 1; i <= n; ++ i)
dist[i] = INF;
dist[s] = 0;
_queue.push(make_pair(0, s));
int node;
while (!_queue.empty()) {
node = _queue.top().second;
_queue.pop();
if (vis[node])
continue;
vis[node] = true;
for (auto edge: graph[node]) {
int other = edges[edge].other(node);
int cost = edges[edge].cost + potentials[node] - potentials[other];
if (edges[edge].flow < edges[edge].cap && dist[node] + cost < dist[other]) {
dist[other] = dist[node] + cost;
father[other] = edge;
_queue.push(make_pair(-dist[other], other));
}
}
}
for (int i = 1; i <= n; ++ i)
dist[i] += (potentials[i] - potentials[s]);
return vis[t];
}
pair <int, int> JohnsonDinic() {
memset(potentials, 0, (n + 1) * sizeof(int));
Bellman();
int flow = 0, cost = 0;
while (Dijkstra()) {
int node = t;
int minimum = INF;
int sum = 0;
while (node != s) {
minimum = min(minimum, edges[father[node]].cap - edges[father[node]].flow);
sum += edges[father[node]].cost;
node = edges[father[node]].other(node);
}
node = t;
while (node != s) {
edges[father[node]].flow += minimum;
edges[father[node] ^ 1].flow -= minimum;
node = edges[father[node]].other(node);
}
flow += minimum;
cost += minimum * sum;
memcpy(potentials, dist, (n + 1) * sizeof(int));
}
return make_pair(flow, cost);
}
};
const int NMAX = 350 + 5;
const int MMAX = 12500 + 5;
MaxFlowMinCost <NMAX, MMAX> f;
int main()
{
ifstream cin("fmcm.in");
ofstream cout("fmcm.out");
int n, m, s, t;
cin >> n >> m >> s >> t;
f.setN(n);
f.setS(s);
f.setT(t);
while (m --) {
int a, b, cap, cost;
cin >> a >> b >> cap >> cost;
f.addEdge(a, b, cap, cost);
}
pair <int, int> ans = f.computeFlow();
cout << ans.second << '\n';
return 0;
}