#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define mt make_tuple
#define debug cerr << "-- " << __LINE__ << " --" << endl
#define MOD 1000000007
typedef int ll;

using namespace std;
typedef vector<int> vi;
typedef vector<vi> vvi;
template <typename T> string to_string(const vector<T> &xs) {string s="["; for(int i=0;i<xs.size();i++) s += (i>0 ? " " : "") + to_string(xs[i]); return s + "]";}
long long nextLL() {long long x; scanf("%lld", &x); return x;}
int nextInt() {int x; scanf("%d", &x); return x;}



class Tree{
	/*
		木
		LCA したい人用
	*/
public:
	int V; // 頂点数
	int E; // 辺数
	vector<vector<ll>> adjList; // 隣接リスト．
	map<pair<ll, ll>, ll> adjMat; // 隣接行列（疎）

	// 根つき木用
	int root = -1; // 「根」とみなす頂点番号．
	vector<vector<ll>> ancestor; // ダブリング先祖．[0] で親，[1] で親^2，[2] で親^4 が取れる
	vector<vector<ll>> children; // 子供の一覧
	vector<ll> nextSibling; // 次のきょうだい．存在しない場合は -1
	vector<ll> numDescendant; // 子孫の個数
	vector<ll> depth; // 深さ（辺数で測った根からの距離）
	vector<ll> farness; // 遠さ（コストで測った根からの距離）
	vector<ll> preOrder; // dfs した時の行きがけ順
	vector<ll> preOrderIndex; // dfs した時の行きがけ順のインデックス
	vector<ll> postOrder; // dfs した時の帰りがけ順
	vector<ll> postOrderIndex; // dfs した時の帰りがけ順のインデックス

	Tree(){};
	Tree(const vector<ll> &st, const vector<ll> &ed, const vector<ll> &cost, ll root){
		/*
			st, ed, root は 0-index．辺の向きは問わない．
			辺数 E と頂点数 V は辺の数から推論される．頂点番号に抜けがあってはならない．
			cost は省略可能（移譲コンストラクタによる．全辺のコストは 1 となる）

			引数に渡された辺情報が木になっていない場合の動作は保障されない．
			（例えば多重辺があったり自己ループがあったり・・・）

			LCA 用の前計算などするので初期化に O(V log V) かかる．
		*/
		assert(st.size() == ed.size());
		E = st.size();
		V = st.size() + 1;

		// adjList と adjMat を作成
		adjList = vector<vector<ll>>(V);
		for(int i=0;i<E;i++){
			adjList[st[i]].push_back(ed[i]);
			adjList[ed[i]].push_back(st[i]);
			adjMat.insert(make_pair(make_pair(st[i], ed[i]), cost[i]));
			adjMat.insert(make_pair(make_pair(ed[i], st[i]), cost[i]));
		}

		// root から BFS して depth と farness と children と ancestor を計算
		this->root = root;
		depth = vector<ll>(V, -1);
		farness = vector<ll>(V, -1);
		children = vector<vector<ll>>(V);
		ancestor = vector<vector<ll>>(V);
		depth[root] = 0;
		farness[root] = 0;
		queue<ll> q; q.push(root);
		while(!q.empty()){
			ll now = q.front(); q.pop();
			for(auto &next : adjList[now]){
				if (depth[next] >= 0) continue;
				depth[next] = depth[now] + 1;
				farness[next] = farness[now] + adjMat[make_pair(now, next)];
				q.push(next);
				children[now].push_back(next);
				ancestor[next].push_back(now); // parent
			}
			for(ll i=1;;i++){
				if ((1 << i) > depth[now]) break;
				ancestor[now].push_back(ancestor[ancestor[now][i - 1]][i - 1]);
			}			
		}

		// nextSibling の計算
		nextSibling = vector<ll>(V, -1);
		for(auto &c : children)for(int j=0;j+1<c.size();j++){
			nextSibling[c[j]] = c[j+1];
		}

		// root から dfs して preOrder, postOrder を計算
		preOrder = vector<ll>();
		postOrder = vector<ll>();
		preOrderIndex = vector<ll>(V);
		postOrderIndex = vector<ll>(V);
		_computeOrder(root);

		// root から dfs して 子孫数 numDescendant を計算
		numDescendant = vector<ll>(V);
		_computeNumDescendant(root); // root を計算すれば再帰的に木全体が計算される

	}
	// コストが省略された場合のコンストラクタ
	Tree(const vector<ll> &st, const vector<ll> &ed, ll root): Tree(st, ed, vector<ll>(st.size(), 1), root){}

	void _computeOrder(ll idx){
		preOrderIndex[idx] = preOrder.size(); preOrder.push_back(idx);
		for(auto &c : children[idx]) _computeOrder(c);
		postOrderIndex[idx] = postOrder.size(); postOrder.push_back(idx);
	}

	void _computeNumDescendant(ll idx){
		numDescendant[idx] = 0;
		for(auto &c : children[idx]){
			_computeNumDescendant(c);
			numDescendant[idx] += 1 + numDescendant[c];
		}
	}

	void _dfs(ll idx){ // dfs のテンプレート．_dfs(root) から再帰開始
		// preOrder
		for(auto &c : children[idx]) _dfs(c);
		// postOrder
	}

	ll parent(ll idx) {
		return (ancestor[idx].size() > 0) ? ancestor[idx][0] : -1;
	}

	ll nthAncestor(ll idx, ll n){
		/*
			頂点 idx から親を n 回辿った先祖を求める．O(log n)
			n==0 の場合は idx が返る．
			存在しない場合は -1 が返る．
		*/
		assert(0 <= idx && idx < V);
		assert(0 <= n);
		if (depth[idx] < n) return -1;
		if (n == 0) return idx;
		ll p = 63LL - __builtin_clzll(n);
		return nthAncestor(ancestor[idx][p], n - (1LL << p));
	}

	ll LCA(ll a, ll b){
		/*
			頂点 a と b の最小共通祖先(LCA)を求める．O(log V)
		*/
		assert(0 <= a && a < V);
		assert(0 <= b && b < V);
		ll ok = 0, ng = min(depth[a], depth[b]) + 1;
		while (ng - ok > 1){
			ll mid = (ok + ng) / 2;
			if (nthAncestor(a, depth[a] - mid) == nthAncestor(b, depth[b] - mid)){
				ok = mid;
			}else{
				ng = mid;
			}
		}
		return nthAncestor(a, depth[a] - ok);
	}
};



ll N, Q;
vector<ll> a, b, c, d, x, y, u, v;


// 木
Tree tr;

//色情報
map<pair<ll, ll>, ll> color;

// 平方分割用
ll m;
vector<ll> isCheckpoint; // 0: チェックポイントではない，1: 未計算チェックポイント，2: 計算済チェックポイント
vector<map<ll, ll>> memoCount;
// チェックポイントなる i に対しては，memo[i] に根から i までの「各色の辺数」が入った辞書が入る予定
vector<map<ll, ll>> memoLength;
// チェックポイントなる i に対しては，memo[i] に根から i までの「各色の辺の長さ」が入った辞書が入る予定


pair<map<ll, ll>, map<ll, ll>> compute(ll a, ll c){
	/*
		色 c が 0 以上の場合は，色 c の情報しか正確性を保証しません（そのぶん高速）．
		c が負の場合は，全部の色の情報を計算します．
	*/
	map<ll, ll> retCount, retLength;
	if (isCheckpoint[a] == 2){
		if (c < 0){
			retCount = memoCount[a];
			retLength = memoLength[a];
		}else{
			retCount[c] = memoCount[a][c];
			retLength[c] = memoLength[a][c];			
		}
	}else{
		ll now = a;
		while(now != tr.root){
			ll pa = tr.ancestor[now][0];
			ll ec = color[mp(pa, now)];
			// if (retCount.find(ec) == retCount.end()) retCount[ec] = 0;
			retCount[ec]++; // カウンタとして使える？
			// if (retLength.find(ec) == retLength.end()) retLength[ec] = 0;
			retLength[ec] += tr.adjMat[mp(pa, now)]; // カウンタとして使える？
			now = pa;
			if (isCheckpoint[now]){
				map<ll, ll> retCount2, retLength2;
				if (isCheckpoint[now] == 2){
					retCount2 = memoCount[now];
					retLength2 = memoLength[now];
				}else{
					auto res = compute(now, -1);
					retCount2 = res.first;
					retLength2 = res.second;
				}
				// ret += ret2 を行う
				if (c < 0){
					for(auto &p : retCount2){
						// if (retCount.find(p.first) == retCount.end()) retCount[p.first] = 0;
						retCount[p.first] += p.second;
					}
					for(auto &p : retLength2){
						// if (retLength.find(p.first) == retLength.end()) retLength[p.first] = 0;
						retLength[p.first] += p.second;
					}
				}else{
					retCount[c] += retCount2[c];
					retLength[c] += retLength2[c];
				}
				break;
			}
		}
		if (isCheckpoint[a] == 1 && c < 0){
			memoCount[a] = retCount;
			memoLength[a] = retLength;
			isCheckpoint[a] = 2;
		}
	}
	return mp(retCount, retLength);
}

pair<ll, ll> coloredEdgeInfo(ll a, ll c){
	/*
		a から root までにある 色 c の辺の「辺数」と「総距離」を返す．
	*/
	auto res = compute(a, c);
	return mp(res.first[c], res.second[c]);
}


vector<map<ll, ll>> deltaCost; // [[[pos, △], ...(pos ascending)], ...(color)]
vector<map<ll, ll>> deltaCount; // [[[pos, △], ...(pos ascending)], ...(color)]

pair<ll, ll> solve(ll node, ll c){ // node から root までの色 c の辺のコストと個数を求める．
	map<ll, ll> &costMap = deltaCost[c];
	map<ll, ll> &countMap = deltaCount[c];

	ll idx = tr.preOrderIndex[node];
	auto itr = costMap.upper_bound(idx);
	ll cost = (itr != costMap.begin()) ? (--itr)->second : 0LL;
	auto itr2 = countMap.upper_bound(idx);
	ll count = (itr2 != countMap.begin()) ? (--itr2)->second : 0LL;
	return mp(count, cost);
}



int main(void){
	/*
	vector<ll> st = {4, 1, 1, 3, 3, 7, 6, 0};
	vector<ll> ed = {1, 2, 8, 1, 5, 0, 0, 2};
	Tree tr = Tree(st, ed, 5);
	cout << tr.LCA(4, 5) << endl;
	cout << tr.LCA(6, 8) << endl;
	cout << tr.LCA(1, 3) << endl;
	cout << tr.LCA(8, 2) << endl;
	cout << tr.LCA(1, 1) << endl;
	cout << tr.LCA(8, 8) << endl;
	cout << tr.LCA(0, 0) << endl;

	cout << to_string(tr.preOrder) << endl;
	cout << to_string(tr.preOrderIndex) << endl;
	*/
	

	cin >> N >> Q;
	for(int i=0;i<N-1;i++){
		a.push_back(nextInt() - 1);
		b.push_back(nextInt() - 1);
		c.push_back(nextInt() - 1);
		d.push_back(nextInt());
	}
	for(int i=0;i<Q;i++){
		x.push_back(nextInt() - 1);
		y.push_back(nextInt());
		u.push_back(nextInt() - 1);
		v.push_back(nextInt() - 1);		
	}

	tr = Tree(a, b, d, 0);


	deltaCost = vector<map<ll, ll>>(N-1);
	deltaCount = vector<map<ll, ll>>(N-1);
	for(int i=0;i<N-1;i++){
		if (tr.depth[a[i]] < tr.depth[b[i]]) swap(a[i], b[i]);
		deltaCost[c[i]][tr.preOrderIndex[a[i]]] += d[i];
		deltaCost[c[i]][tr.preOrderIndex[a[i]] + 1 + tr.numDescendant[a[i]]] -= d[i];
		deltaCount[c[i]][tr.preOrderIndex[a[i]]] += 1;
		deltaCount[c[i]][tr.preOrderIndex[a[i]] + 1 + tr.numDescendant[a[i]]] -= 1;
	}
	for(int i=0;i<N-1;i++){
		// sort(deltaCost[i].begin(), deltaCost[i].end());
		// sort(deltaCount[i].begin(), deltaCount[i].end());
		ll sum = 0;
		for(auto &p : deltaCost[i]){
			sum += p.second; p.second = sum;
		}
		sum = 0;
		for(auto &p : deltaCount[i]){
			sum += p.second; p.second = sum;
		}
	}

	for(int i=0;i<Q;i++){
		ll s = u[i], t = v[i], lca = tr.LCA(u[i], v[i]);
		ll ans = tr.farness[s] + tr.farness[t] - 2 * tr.farness[lca]; //補正前
		// cout << ans << endl;
		auto info1 = solve(s, x[i]);
		auto info2 = solve(t, x[i]);
		auto info3 = solve(lca, x[i]);
		ll count = info1.first + info2.first - 2 * info3.first;
		ll length = info1.second + info2.second - 2 * info3.second;

		ans += count * y[i] - length;

		cout << ans << endl;
	}


	/*
	for(int i=0;i<N-1;i++){
		color.insert(mp(mp(a[i], b[i]), c[i]));
		color.insert(mp(mp(b[i], a[i]), c[i]));
	}

	// 平方分割っぽいことをやる（と言いつつ全頂点になってる・・・）
	m = max(1, (ll)sqrt(N) / 200);
	isCheckpoint = vector<ll>(N); // 0: チェックポイントではない，1: 未計算チェックポイント，2: 計算済チェックポイント
	for(int i=0;i<N;i++) isCheckpoint[i] = (rand()%m == 0);
	memoCount = vector<map<ll, ll>>(N);
	memoLength = vector<map<ll, ll>>(N);
	// チェックポイントなる i に対しては，memo[i] に根から i までの「各色の辺数」が入った辞書が入る予定
	
	for(int i=0;i<Q;i++){
		ll s = u[i], t = v[i], lca = tr.LCA(u[i], v[i]);

		ll ans = tr.farness[s] + tr.farness[t] - 2 * tr.farness[lca]; //補正前
		// cout << ans << endl;

		auto info1 = coloredEdgeInfo(s, x[i]);
		auto info2 = coloredEdgeInfo(t, x[i]);
		auto info3 = coloredEdgeInfo(lca, x[i]);
		ll count = info1.first + info2.first - 2 * info3.first;
		ll length = info1.second + info2.second - 2 * info3.second;

		ans += count * y[i] - length;

		cout << ans << endl;
	}
	*/

    return 0;
}

Measured memory_bytes: 66020