#define _USE_MATH_DEFINES
#include "bits/stdc++.h"
using namespace std;

//template
#define rep(i,a,b) for(int i=(a);i<(b);i++)
#define rrep(i,a,b) for(int i=(a);i>(b);i--)
typedef long long int ll;
typedef pair<ll, ll> P; typedef pair<ll, P> T;
typedef complex<double> com;
struct edge { int u, v, cost; };
template<class T> inline bool chmax(T& a, T b) { if (a < b) { a = b; return 1; } return 0; }
template<class T> inline bool chmin(T& a, T b) { if (a > b) { a = b; return 1; } return 0; }
ll gcd(ll a, ll b) { if (!b)return a; else return gcd(b, a % b); }
const int inf = INT_MAX / 2; const ll INF = LLONG_MAX / 2;
int mod = 1e9 + 7; //998244353;
struct Mint {
	int val;
	Mint inv() const {
		unsigned tmp, a = val, b = mod; int x = 1, y = 0;
		while (b) tmp = a / b, a -= tmp * b, swap(a, b), x -= tmp * y, swap(x, y);
		return Mint(x);
	}
public:
	Mint() :val(0) {}
	Mint(ll x) :val(x >= 0 ? x % mod : x % mod + mod) {}
	int mtoi() { return this->val; }
	Mint pow(ll t) { Mint res = 1; while (t > 0) { if (t & 1) res *= *this; *this *= *this; t >>= 1; }return res; }
	Mint& operator+=(const Mint& x) { if ((val += x.val) >= mod) val -= mod; return *this; }
	Mint& operator-=(const Mint& x) { if ((val += mod - x.val) >= mod) val -= mod; return *this; }
	Mint& operator*=(const Mint& x) { val = (ll)val * x.val % mod; return *this; }
	Mint& operator/=(const Mint& x) { return *this *= x.inv(); }
	bool operator==(const Mint& x) const { return val == x.val; }
	bool operator!=(const Mint& x) const { return val != x.val; }
	bool operator<(const Mint& x) const { return val < x.val; }
	bool operator<=(const Mint& x) const { return val <= x.val; }
	bool operator>(const Mint& x) const { return val > x.val; }
	bool operator>=(const Mint& x) const { return val >= x.val; }
	Mint operator-() const { return Mint(-val); }
	Mint operator+(const Mint& x) const { return Mint(*this) += x; }
	Mint operator-(const Mint& x) const { return Mint(*this) -= x; }
	Mint operator*(const Mint& x) const { return Mint(*this) *= x; }
	Mint operator/(const Mint& x) const { return Mint(*this) /= x; }
};
struct factorial {
	vector<Mint> Fact, Finv;
public:
	factorial(int maxx) {
		Fact.resize(maxx + 1, Mint(1)); Finv.resize(maxx + 1);
		rep(i, 0, maxx) Fact[i + 1] = Fact[i] * Mint(i + 1);
		Finv[maxx] = Mint(1) / Fact[maxx];
		rrep(i, maxx, 0) Finv[i - 1] = Finv[i] * Mint(i);
	}
	Mint fact(int n) { return Fact[n]; }
	Mint finv(int n) { return Finv[n]; }
	Mint nCr(int n, int r) {
		if (n < 0 || n < r || r < 0) return Mint(0);
		return Fact[n] * Finv[r] * Finv[n - r];
	}
	Mint nPr(int n, int r) {
		if (n < 0 || n < r || r < 0) return Mint(0);
		return Fact[n] * Finv[n - r];
	}
};
class UnionFind {
	vector<int> par;
public:
	UnionFind(int n) { par = vector<int>(n, -1); }
	int root(int a) {
		if (par[a] < 0) return a;
		else return par[a] = root(par[a]);
	}
	int size(int a) { return -par[root(a)]; }
	bool connect(int a, int b) {
		a = root(a), b = root(b);
		if (a == b) return false;
		if (size(a) < size(b)) swap(a, b);
		par[a] += par[b], par[b] = a;
		return true;
	}
};
struct max_flow {
	struct Edge { int to, cap, rev; };
	int V; vector<vector<Edge>> G; vector<int> itr, level;
public:
	max_flow(int V) : V(V) { G.assign(V, vector<Edge>()); }
	void add_edge(int from, int to, int cap) {
		G[from].push_back({ to, cap, (int)G[to].size() });
		G[to].push_back({ from, 0, (int)G[from].size() - 1 });
	}
	void bfs(int s) {
		level.assign(V, -1); queue<int> q;
		level[s] = 0; q.push(s);
		while (!q.empty()) {
			int v = q.front(); q.pop();
			for (auto& e : G[v]) {
				if (e.cap > 0 && level[e.to] < 0) {
					level[e.to] = level[v] + 1;
					q.push(e.to);
				}
			}
		}
	}
	int dfs(int v, int t, int f) {
		if (v == t) return f;
		for (int& i = itr[v]; i < (int)G[v].size(); ++i) {
			Edge& e = G[v][i];
			if (e.cap > 0 && level[v] < level[e.to]) {
				int d = dfs(e.to, t, min(f, e.cap));
				if (d > 0) {
					e.cap -= d, G[e.to][e.rev].cap += d;
					return d;
				}
			}
		}
		return 0;
	}
	int run(int s, int t) {
		int ret = 0, f;
		while (bfs(s), level[t] >= 0) {
			itr.assign(V, 0);
			while ((f = dfs(s, t, inf)) > 0) ret += f;
		}
		return ret;
	}
};
vector<ll> dijkstra(vector<vector<edge>> Grp, int s) {
	priority_queue<P, vector<P>, greater<P> > que;
	vector<ll> dist(Grp.size(), INF); dist[s] = 0; que.push(P(0, s));
	while (!que.empty()) {
		P p = que.top(); que.pop();
		int v = p.second; if (dist[v] < p.first) continue;
		rep(i, 0, Grp[v].size()) {
			edge e = Grp[v][i];
			if (dist[e.v] > dist[v] + e.cost) {
				dist[e.v] = dist[v] + e.cost;
				que.push(P(dist[e.v], e.v));
			}
		}
	} return dist;
}
void fft(vector<com>& x, bool inv) {
	int s = x.size(); if (s == 1) return;
	vector<com> even(s / 2), odd(s / 2);
	rep(i, 0, s / 2)even[i] = x[i * 2], odd[i] = x[i * 2 + 1];
	fft(even, inv), fft(odd, inv);
	com w = 1, w_0 = polar(1.0, (inv ? -1 : 1) * 2LL * M_PI / s);
	rep(i, 0, s)x[i] = even[i % (s / 2)] + w * odd[i % (s / 2)], w *= w_0;
}
void MultiFunction(vector<int>& a, vector<int>& b) {
	int n = a.size(), maxx = 1; while (maxx <= 2 * n)maxx *= 2;
	vector<com> x(maxx), y(maxx);
	rep(i, 0, n) x[i] = com(a[i], 0), y[i] = com(b[i], 0);
	fft(x, 0), fft(y, 0); rep(i, 0, maxx) x[i] *= y[i];
	fft(x, 1); rep(i, 0, maxx)x[i] /= maxx;
	a.resize(2 * n - 1, 0);
	rep(i, 0, 2 * n - 1)a[i] = (int)(x[i].real() + 0.5);
}
ll merge_cnt(vector<int> a) {
	ll n = a.size(), ai = 0, bi = 0, ci = 0, cnt = 0;
	if (n <= 1) return 0;
	vector<int> b(a.begin(), a.begin() + n / 2);
	vector<int> c(a.begin() + n / 2, a.end());
	cnt += merge_cnt(b) + merge_cnt(c);
	while (ai < n) {
		if (bi < b.size() && (ci == c.size() || b[bi] <= c[ci]))a[ai++] = b[bi++];
		else cnt += n / 2LL - bi, a[ai++] = c[ci++];
	}
	return cnt;
}
//template end

struct Edge { int v, col, len; };
vector<Edge> g[100010];
int root;
vector<int> parent[17],depth;
void DFS(int v, int p, int d) {
	parent[0][v] = p;
	depth[v] = d;
	for (Edge u : g[v])if (u.v != p)DFS(u.v, v, d + 1);
}
void init(int V) {
	rep(i, 0, 17)parent[i].resize(V);
	depth.resize(V, -1);
	DFS(root, -1, 0);
	rep(k, 0, 16)rep(v, 0, V) {
		if (parent[k][v] < 0)parent[k + 1][v] = -1;
		else parent[k + 1][v] = parent[k][parent[k][v]];
	}
}
int lca(int u, int v) {
	if(depth[u] > depth[v]) swap(u, v);
	rep(k, 0, 17) if ((depth[v] - depth[u]) >> k & 1) v = parent[k][v];
	if (u == v)return u;
	rrep(k, 16, -1) {
		if (parent[k][u] != parent[k][v]) {
			u = parent[k][u];
			v = parent[k][v];
		}
	}
	return parent[0][u];
}
vector<int> color[100010];
map<int, int> colnum[100010], colsum[100010];
int dist[100010], num[100010], sum[100010];
//queryに必要なcolorに関する情報のみをdfsして、mapで管理する
void dfs(int v, int p) {
	for (int col : color[v]) {
		colnum[v][col] = num[col];
		colsum[v][col] = sum[col];
	}
	for (Edge e : g[v]) {
		int u = e.v, col = e.col, d = e.len;
		if (u == p)continue;
		dist[u] = dist[v] + d;
		num[col]++; sum[col] += d;
		dfs(u, v);
		num[col]--; sum[col] -= d;
	}
}

int main() {
	int n, q; cin >> n >> q;
	rep(i, 0, n - 1) {
		int a, b, c, d; cin >> a >> b >> c >> d;
		a--; b--; c--;
		g[a].push_back({ b,c,d });
		g[b].push_back({ a,c,d });
	}
	init(n);
	vector<vector<int>> query(q);
	rep(i, 0, q) {
		int x, y, u, v; cin >> x >> y >> u >> v;
		x--; u--; v--; int l = lca(u, v);
		query[i] = { x,y,u,v,l };
		color[u].push_back(x);
		color[v].push_back(x);
		color[l].push_back(x);
	}
	dfs(0, -1);
	rep(i, 0, q) {
		int x = query[i][0], y = query[i][1], u = query[i][2], v = query[i][3], l = query[i][4];
		int du = dist[u] - colsum[u][x] + colnum[u][x] * y;
		int dv = dist[v] - colsum[v][x] + colnum[v][x] * y;
		int dl = dist[l] - colsum[l][x] + colnum[l][x] * y;
		printf("%d\n", du + dv - 2 * dl);
	}
	return 0;
}


Measured memory_bytes: 62776