#include <algorithm>
#include <vector>
#include <unordered_map>
#include <queue>
#include <functional>
#include <climits>
#include <iostream>


class edge {
public:
	edge(int to, long long cost);
	int get_to() const;
	long long get_cost() const;
private:
	int to;
	long long cost;
};

edge::edge(int to, long long cost) : to(to), cost(cost) {}

int edge::get_to() const { return to; }

long long edge::get_cost() const { return cost; }


class dykstra {
private:
	//unordered_mapを使って隣接を構築
	//first:接続元、second:辺のコストと接続先
	std::unordered_map<int, std::vector<edge>> adj_list;
	int node_num;
	int start_point;
	std::vector<int> pred;//経路（遷移元）を保持
	std::vector<long long> costs;//距離を保持
	std::vector<bool> is_done;//確定ノードか否かを保持

public:
	dykstra(const int node_num, const std::unordered_map<int, std::vector<edge>>& adj_list);
	void calc_min_cost(int start);
	long long get_cost(int end) const;
	std::vector<int> get_min_path(int end) const;
};

dykstra::dykstra(const int node_num, const std::unordered_map<int, std::vector<edge>>& adj_list) :
	adj_list(adj_list),
	start_point(0),
	node_num(node_num),
	pred(node_num + 1, INT_MAX),
	costs(node_num + 1, LLONG_MAX),
	is_done(node_num + 1, false)
{}

void dykstra::calc_min_cost(int start) {
	this->start_point = start;
	this->pred = std::vector<int>(node_num + 1, INT_MAX);
	this->costs = std::vector<long long>(node_num + 1, LLONG_MAX);
	this->is_done = std::vector<bool>(node_num + 1, false);

	//優先度付きキューpairで頂点番号と最短距離を保持
	//firstの要素で比較されるので、firstが距離、secondを頂点番号とする
	std::priority_queue<std::pair<long long, int>,
		std::vector<std::pair<long long, int>>, std::greater<std::pair<long long, int>>> p_queue;
	p_queue.push(std::make_pair(0, start));
	pred[start] = -1;
	costs[start] = 0;

	while (1) {
		if (p_queue.empty() == true) break; //キューが空なら終了
		std::pair<long long, int> cost_node = p_queue.top();
		p_queue.pop();
		is_done[cost_node.second] = true; //キューから取り出した頂点を確定頂点とする
		for (auto &it : adj_list[cost_node.second]) {
			int adj_node = it.get_to();
			if (is_done[adj_node] == false) {
				long long adj_cost = it.get_cost() + cost_node.first;
				if (adj_cost < costs[adj_node]) {//計算された隣接頂点のコストが現在のコストより小さいなら
					costs[adj_node] = adj_cost; //隣接頂点のコストを更新
					pred[adj_node] = cost_node.second;//隣接頂点の遷移元を更新
					p_queue.push(std::make_pair(adj_cost, adj_node));//キューに隣接頂点の情報を突っ込む
				}
			}
		}
	}
}

long long dykstra::get_cost(int end) const {
	return costs[end];
}

std::vector<int> dykstra::get_min_path(int end) const {
	int node = end;
	std::vector<int> vec;
	vec.push_back(node);
	//終点から始点までの経路をたどる
	while (1) {
		//始点から辿れない頂点or
		//始点であれば終了
		if (pred[node] >= INT_MAX || pred[node] == -1) break;
		node = pred[node];
		vec.push_back(node);
	}
	std::reverse(vec.begin(), vec.end());
	return vec;
}

int main()
{
	std::unordered_map<int, std::vector<edge>> adj_list_yen;
	std::unordered_map<int, std::vector<edge>> adj_list_snuke;
	int n, m, s, t;
	std::cin >> n >> m >> s >> t;
	for (int i = 0; i < m; i++) {
		int u, v;
		long long a, b;
		std::cin >> u >> v >> a >> b;

		adj_list_yen[u].push_back(edge(v, a));
		adj_list_yen[v].push_back(edge(u, a));
		adj_list_snuke[v].push_back(edge(u, b));
		adj_list_snuke[u].push_back(edge(v, b));
	}

	dykstra d_yen(n + 1, adj_list_yen);
	dykstra d_snuke(n + 1, adj_list_snuke);

	d_yen.calc_min_cost(s);
	d_snuke.calc_min_cost(t);

	std::vector<std::pair<long long, int>> min_costs;
	
	for (int i = 1; i <= n; i++) {
		min_costs.push_back(std::make_pair( static_cast <long long>(1e+15) - d_yen.get_cost(i) - d_snuke.get_cost(i), i));
	}

	std::sort(min_costs.begin(), min_costs.end(), std::greater<std::pair<long long, int>>());
	int count = 0;
	for (int i = 0; i < n; i++) {
		while (1) {
			if (min_costs[count].second > i) {
				std::cout << min_costs[count].first << std::endl;
				break;
			}
			else
				count++;
		}
	}

	return 0;
}



Measured memory_bytes: 51492