#include <bits/stdc++.h>
using namespace std;
const int MX = 1e5+1;
struct edge {
	int v, c, w;
};
vector<edge> adj[MX];
struct HLD {
	vector<int> seg, parent, depth, head, pos, heavy;
	int curpos;
	vector<tuple<int, int, int>> mst[MX*2]; // color, count of color, weight of color
	int n;
	HLD(int _n) {
		n = _n;
		seg.resize(2*MX);
		parent.resize(MX);
		depth.resize(MX);
		head.resize(MX);
		pos.resize(MX);
		heavy.resize(MX, -1);
		curpos = 0;
		dfs(0);
		decompose(0, 0);
		for(int i=n-1; i; i--) {
			seg[i] = seg[2*i]+seg[2*i+1];
			int l = 0, r = 0;
			auto &lv = mst[2*i], &rv = mst[2*i+1], &v = mst[i];
			while(l < lv.size() && r < rv.size()) {
				int lx, ly, lz, rx, ry, rz;
				tie(lx, ly, lz) = lv[l];
				tie(rx, ry, rz) = rv[r];
				if(lx < rx) {
					v.push_back(lv[l]);
					l++;
				} else if(lx > rx) {
					v.push_back(rv[r]);
					r++;
				} else {
					v.emplace_back(lx, ly+ry, lz+rz);
					l++;
					r++;
				}
			}
			while(l < lv.size()) {
				v.push_back(lv[l++]);
			}
			while(r < rv.size()) {
				v.push_back(rv[r++]);
			}
		}
	}
	int dfs(int v) {
		int size = 1, max_c_size = 0;
		for(auto e: adj[v]) {
			if(e.v == parent[v]) continue;
			parent[e.v] = v, depth[e.v] = depth[v]+1;
			int c_size = dfs(e.v);
			size += c_size;
			if(c_size > max_c_size)
				max_c_size = c_size, heavy[v] = e.v;
		}
		return size;
	}
	void decompose(int v, int h) {
	    head[v] = h, pos[v] = curpos++;
	    //printf("node %d assigned pos %d\n", v+1, pos[v]);
	    if (heavy[v] != -1)
	        decompose(heavy[v], h);
	    for (auto e: adj[v]) {
	    	if(e.v == parent[v]) continue;
	    	if(e.v != heavy[v])
	    		decompose(e.v, e.v);
	    	seg[pos[e.v]+n] = e.w;
	    	mst[pos[e.v]+n].emplace_back(e.c, 1, e.w);
	    }
	}
	int seg_query(int a, int b) {
		if(a > b) swap(a, b);
		a += n, b += n;
		int ret = 0;
		while(a < b) {
			if(a&1) ret += seg[a++];
			if(b&1) ret += seg[--b];
			a /= 2, b /= 2;
		}
		//printf("  segquery returns allweight=%d\n", ret);
		return ret;
	}
	void sub_mst(vector<tuple<int,int,int>>& v, int c, int& cnt, int& wtot) {
		tuple<int, int, int> tmp = make_tuple(c, 0, 0);
		auto it = lower_bound(v.begin(), v.end(), tmp);
		if(it != v.end() && get<0>(*it) == c) {
			cnt += get<1>(*it);
			wtot += get<2>(*it);
		}
	}
	void mst_query(int a, int b, int c, int& cnt, int& wtot) {
		if(a > b) swap(a, b);
		a += n, b += n;
		while(a < b) {
			if(a&1) {
				sub_mst(mst[a], c, cnt, wtot);
				a++;
			}
			if(b&1) {
				--b;
				sub_mst(mst[b], c, cnt, wtot);
			}
			a /= 2, b /= 2;
		}
		//printf("  after mst query: color cnt=%d, color tot=%d\n", cnt, wtot);
	}
	int query(int color, int weight, int a, int b) {
		int total = 0, colortotal = 0, colorcount = 0;
		for(;head[a] != head[b]; b = parent[head[b]]) {
			if(depth[head[a]] > depth[head[b]])
				swap(a, b);
			//printf("a=%d, b=%d. querying from b, position is %d~%d\n", a+1, b+1, pos[head[b]], pos[b]);
			total += seg_query(pos[head[b]], pos[b]+1);
			mst_query(pos[head[b]], pos[b]+1, color, colorcount, colortotal);
		}
		//printf("a=%d, b=%d. last query from a to b: position is %d~%d\n", a+1, b+1, pos[a], pos[b]);
		total += seg_query(pos[a]+1, pos[b]+1);
		mst_query(pos[a]+1, pos[b]+1, color, colorcount, colortotal);
		//printf("finally, total=%d, colortotal=%d, colorcount=%d. weight=%d\n", total, colortotal, colorcount, weight);
		return total-colortotal+colorcount*weight;
	}
};
 
int main() {
	ios::sync_with_stdio(0);cin.tie(0);
	int n, q;
	cin >> n >> q;
	for(int i=0; i<n-1; i++) {
		int a, b, c, d;
		cin >> a >> b >> c >> d;
		adj[a-1].push_back({b-1, c, d});
		adj[b-1].push_back({a-1, c, d});
	}
	HLD hld(n);
	for(int i=0; i<q; i++) {
		int x, y, u, v;
		cin >> x >> y >> u >> v;
		//printf("START\n");
		cout << hld.query(x, y, u-1, v-1) << '\n';
		//printf("END\n\n");
	}
}

Measured memory_bytes: 42936