AtCoder Beginner Contest 133 - Colorful Tree

There is a tree with N vertices numbered 1 to N.
The i-th edge in this tree connects Vertex a_i and Vertex b_i, and the color and length of that edge are c_i and d_i, respectively.
Here the color of each edge is represented by an integer between 1 and N-1 (inclusive). The same integer corresponds to the same color, and different integers correspond to different colors.
Answer the following Q queries:
 - Query j (1 \leq j \leq Q): assuming that the length of every edge whose color is x_j is changed to y_j, find the distance between Vertex u_j and Vertex v_j. (The changes of the lengths of edges do not affect the subsequent queries.)

-----Constraints-----
 - 2 \leq N \leq 10^5
 - 1 \leq Q \leq 10^5
 - 1 \leq a_i, b_i \leq N
 - 1 \leq c_i \leq N-1
 - 1 \leq d_i \leq 10^4
 - 1 \leq x_j \leq N-1
 - 1 \leq y_j \leq 10^4
 - 1 \leq u_j < v_j \leq N
 - The given graph is a tree.
 - All values in input are integers.

-----Input-----
Input is given from Standard Input in the following format:
N Q
a_1 b_1 c_1 d_1
:
a_{N-1} b_{N-1} c_{N-1} d_{N-1}
x_1 y_1 u_1 v_1
:
x_Q y_Q u_Q v_Q

-----Output-----
Print Q lines. The j-th line (1 \leq j \leq Q) should contain the answer to Query j.

-----Sample Input-----
5 3
1 2 1 10
1 3 2 20
2 4 4 30
5 2 1 40
1 100 1 4
1 100 1 5
3 1000 3 4

-----Sample Output-----
130
200
60

The graph in this input is as follows:

Here the edges of Color 1 are shown as solid red lines, the edge of Color 2 is shown as a bold green line, and the edge of Color 4 is shown as a blue dashed line.
 - Query 1: Assuming that the length of every edge whose color is 1 is changed to 100, the distance between Vertex 1 and Vertex 4 is 100 + 30 = 130.
 - Query 2: Assuming that the length of every edge whose color is 1 is changed to 100, the distance between Vertex 1 and Vertex 5 is 100 + 100 = 200.
 - Query 3: Assuming that the length of every edge whose color is 3 is changed to 1000 (there is no such edge), the distance between Vertex 3 and Vertex 4 is 20 + 10 + 30 = 60. Note that the edges of Color 1 now have their original lengths.

Difficulty: MEDIUM_HARD

Starter code:

Solutions:
import sys
input = sys.stdin.readline
sys.setrecursionlimit(10 ** 7)
import numpy as np
(N, Q) = map(int, input().split())
graph = [[] for _ in range(N + 1)]
for _ in range(N - 1):
	(a, b, c, d) = map(int, input().split())
	graph[a].append((b, c, d))
	graph[b].append((a, c, d))
query = [[int(x) for x in input().split()] for _ in range(Q)]

def euler_tour(x, tour_v, tour_d, depth=0, parent=None):
	tour_v.append(x)
	tour_d.append(depth)
	for (y, c, d) in graph[x]:
		if y == parent:
			continue
		euler_tour(y, tour_v, tour_d, depth + 1, parent=x)
		tour_v.append(x)
		tour_d.append(depth)
tour_v = []
tour_d = []
euler_tour(1, tour_v, tour_d)
tour_d_arr = np.array(tour_d)
v_to_i = {v: i for (i, v) in enumerate(tour_v)}
v_to_d = dict(zip(tour_v, tour_d))
L = len(tour_v)
U = L.bit_length()
sp = [np.arange(L)]
for i in range(1, U):
	prev = sp[-1]
	width = 1 << i - 1
	n1 = prev[:-width]
	n2 = prev[width:]
	sp.append(np.where(tour_d_arr[n1] < tour_d_arr[n2], n1, n2))

def LCA(x, y):
	ix = v_to_i[x]
	iy = v_to_i[y]
	if ix > iy:
		(ix, iy) = (iy, ix)
	items = iy - ix + 1
	L = items.bit_length() - 1
	n1 = sp[L][ix]
	n2 = sp[L][iy - (1 << L) + 1]
	n = n1 if tour_d[n1] < tour_d[n2] else n2
	return tour_v[n]
tasks = [[] for _ in range(N + 1)]
for (i, (x, y, u, v)) in enumerate(query):
	tasks[u].append((i, x, y, 1))
	tasks[v].append((i, x, y, 1))
	tasks[LCA(u, v)].append((i, x, y, -2))
answer = [0] * Q

def dfs(x=1, sums=[0] * (N + 1), nums=[0] * (N + 1), total=0, parent=0):
	for (i, c, d, coef) in tasks[x]:
		answer[i] += coef * (total - sums[c] + nums[c] * d)
	for (y, c, d) in graph[x]:
		if y == parent:
			continue
		sums[c] += d
		nums[c] += 1
		total += d
		dfs(y, sums, nums, total, x)
		sums[c] -= d
		nums[c] -= 1
		total -= d
	return
dfs()
print('\n'.join(map(str, answer)))


import sys
sys.setrecursionlimit(10 ** 5 + 5)
from collections import defaultdict, deque
input = sys.stdin.readline

def bfs():
	q = deque([1])
	while q:
		now = q.popleft()
		for (nex, c, w) in e[now]:
			if depth[nex] >= 0:
				continue
			depth[nex] = depth[now] + 1
			dis[nex] = dis[now] + w
			lca[0][nex] = now
			q.append(nex)

def dfs1(now, p, count1, count2):
	for cou in dcount[now]:
		dcount[now][cou] = count1[cou]
	for wei in dweight[now]:
		dweight[now][wei] = count2[wei]
	for (nex, c, w) in e[now]:
		if nex == p:
			continue
		count1[c] += 1
		count2[c] += w
		dfs1(nex, now, count1, count2)
		count1[c] -= 1
		count2[c] -= w
(n, q) = map(int, input().split())
e = [[] for i in range(n + 1)]
for i in range(n - 1):
	(a, b, c, d) = map(int, input().split())
	e[a].append((b, c, d))
	e[b].append((a, c, d))
size = n + 1
bitlen = 19
lca = [[0] * size for i in range(bitlen)]
depth = [-1] * size
dis = [-1] * size
depth[1] = 0
dis[1] = 0
bfs()
for i in range(1, bitlen):
	for j in range(1, size):
		if lca[i - 1][j] > 0:
			lca[i][j] = lca[i - 1][lca[i - 1][j]]

def search(x, y):
	if depth[x] > depth[y]:
		(x, y) = (y, x)
	for i in range(bitlen):
		if depth[y] - depth[x] >> i & 1:
			y = lca[i][y]
	if x == y:
		return x
	for i in range(bitlen - 1, -1, -1):
		if lca[i][x] != lca[i][y]:
			x = lca[i][x]
			y = lca[i][y]
	return lca[0][x]
Q = []
dcount = defaultdict(lambda : defaultdict(int))
dweight = defaultdict(lambda : defaultdict(int))
for i in range(q):
	(x, y, u, v) = map(int, input().split())
	a = search(u, v)
	dcount[u][x] = 0
	dcount[v][x] = 0
	dcount[a][x] = 0
	dweight[u][x] = 0
	dweight[v][x] = 0
	dweight[a][x] = 0
	Q.append((x, y, u, v, a))
dfs1(1, 0, defaultdict(int), defaultdict(int))
for (x, y, u, v, a) in Q:
	cal = dis[u] + dis[v] - 2 * dis[a]
	cal += y * (dcount[u][x] + dcount[v][x] - 2 * dcount[a][x]) - dweight[u][x] - dweight[v][x] + 2 * dweight[a][x]
	print(cal)


import sys
from collections import defaultdict
sys.setrecursionlimit(10 ** 9)
INF = 10 ** 18
MOD = 10 ** 9 + 7
input = lambda : sys.stdin.readline().rstrip()
YesNo = lambda b: bool([print('Yes')] if b else print('No'))
YESNO = lambda b: bool([print('YES')] if b else print('NO'))
int1 = lambda x: int(x) - 1
(N, Q) = map(int, input().split())
edge = [[] for _ in range(N)]
for _ in range(N - 1):
	(a, b, c, d) = map(int1, input().split())
	d += 1
	edge[a].append((b, c, d))
	edge[b].append((a, c, d))
U = defaultdict(list)
V = defaultdict(list)
Query = []
for i in range(Q):
	(x, y, u, v) = map(int1, input().split())
	y += 1
	Query.append((x, y, u, v))
	U[u].append((x, y, i))
	V[v].append((x, y, i))
S = [0] * (2 * N - 1)

def EulerTour(start, n, edges):
	used = [False] * n
	et = []
	left = [-1] * n
	right = [-1] * n
	depth = []
	stack = [(start, -1, -1)]
	cur_depth = -1
	while stack:
		(v, c, d) = stack.pop()
		if et:
			S[len(et)] = S[len(et) - 1] + d
		if v >= 0:
			used[v] = True
			cur_depth += 1
			left[v] = right[v] = len(et)
			et.append(v)
			depth.append(cur_depth)
			for (nv, c, d) in edges[v]:
				if not used[nv]:
					stack.append((~v, c, -d))
					stack.append((nv, c, d))
		else:
			cur_depth -= 1
			right[~v] = len(et)
			et.append(~v)
			depth.append(cur_depth)
	return (et, left, depth)
(et, left, depth) = EulerTour(0, N, edge)

class SegmentTree:

	def __init__(self, n, segfunc, ide_ele):
		self.segfunc = segfunc
		self.ide_ele = ide_ele
		self.num = 2 ** (n - 1).bit_length()
		self.dat = [ide_ele] * 2 * self.num

	def init(self, iter):
		for i in range(len(iter)):
			self.dat[i + self.num] = iter[i]
		for i in range(self.num - 1, 0, -1):
			self.dat[i] = self.segfunc(self.dat[i * 2], self.dat[i * 2 + 1])

	def update(self, k, x):
		k += self.num
		self.dat[k] = x
		while k:
			k //= 2
			self.dat[k] = self.segfunc(self.dat[k * 2], self.dat[k * 2 + 1])

	def query(self, p, q):
		if q <= p:
			return self.ide_ele
		p += self.num
		q += self.num - 1
		res = self.ide_ele
		while q - p > 1:
			if p & 1 == 1:
				res = self.segfunc(res, self.dat[p])
			if q & 1 == 0:
				res = self.segfunc(res, self.dat[q])
				q -= 1
			p = (p + 1) // 2
			q = q // 2
		if p == q:
			res = self.segfunc(res, self.dat[p])
		else:
			res = self.segfunc(self.segfunc(res, self.dat[p]), self.dat[q])
		return res
s = SegmentTree(2 * N - 1, lambda a, b: min(a, b, key=lambda t: t[0]), (INF, INF))
for i in range(2 * N - 1):
	s.update(i, (depth[i], i))
LCA = defaultdict(list)
LLCA = [-1] * Q
for (i, (x, y, u, v)) in enumerate(Query):
	(p, q) = (min(left[u], left[v]), max(left[u], left[v]) + 1)
	res = s.query(p, q)
	LCA[et[res[1]]].append((x, y, i))
	LLCA[i] = res[1]
diff = [0] * Q

def EulerTour2(start, n, edges):
	used = [False] * n
	et = []
	left = [-1] * n
	right = [-1] * n
	depth = []
	stack = [(start, -1, -1)]
	cur_depth = -1
	col = [[0, 0] for _ in range(N)]
	while stack:
		(v, c, d) = stack.pop()
		if v >= 0:
			col[c][0] += d
			col[c][1] += 1
			used[v] = True
			cur_depth += 1
			left[v] = right[v] = len(et)
			et.append(v)
			depth.append(cur_depth)
			for (nv, c, d) in edges[v]:
				if not used[nv]:
					stack.append((~v, c, -d))
					stack.append((nv, c, d))
		else:
			col[c][0] += d
			col[c][1] -= 1
			cur_depth -= 1
			right[~v] = len(et)
			et.append(~v)
			depth.append(cur_depth)
		while LCA[v]:
			(qc, qd, qi) = LCA[v].pop()
			diff[qi] -= (col[qc][1] * qd - col[qc][0]) * 2
		while U[v]:
			(qc, qd, qi) = U[v].pop()
			diff[qi] += col[qc][1] * qd - col[qc][0]
		while V[v]:
			(qc, qd, qi) = V[v].pop()
			diff[qi] += col[qc][1] * qd - col[qc][0]
	return
EulerTour2(0, N, edge)
for (i, (x, y, u, v)) in enumerate(Query):
	print(S[left[v]] + S[left[u]] - S[LLCA[i]] * 2 + diff[i])


import sys
input = sys.stdin.readline
sys.setrecursionlimit(10 ** 8)

class SegmentTree1:

	def __init__(self, n_, ele_id, operation_func):
		self.n = 1 << (n_ - 1).bit_length()
		self.data = [ele_id] * (2 * self.n)
		self.ele_id = ele_id
		self.operation_func = operation_func

	def __getitem__(self, idx):
		return self.data[idx + self.n]

	def build(self, data_init):
		for i in range(len(data_init)):
			self.data[i + self.n] = data_init[i]
		for i in range(self.n - 1, 0, -1):
			self.data[i] = self.operation_func(self.data[2 * i], self.data[2 * i + 1])

	def update(self, idx, x):
		idx += self.n
		self.data[idx] = x
		while idx > 1:
			idx = idx >> 1
			self.data[idx] = self.operation_func(self.data[2 * idx], self.data[2 * idx + 1])

	def query(self, l, r):
		l += self.n
		r += self.n
		ret = self.ele_id
		while l < r:
			if l & 1:
				ret = self.operation_func(ret, self.data[l])
				l += 1
			if r & 1:
				r -= 1
				ret = self.operation_func(ret, self.data[r])
			l = l >> 1
			r = r >> 1
		return ret

class Tree:

	def __init__(self, n, graph, v_root):
		self.n = n
		self.graph = graph
		self.v_root = v_root
		self.first_idx = [2 * self.n] * self.n
		self.euler_tour = []
		self.euler_depth_topo = []
		self.euler_depth_real = []
		self.cs_color = [0] * self.n
		self.euler_tour_dfs(self.v_root, -1, 0, 0)
		depth_list = [(di, i) for (i, di) in enumerate(self.euler_depth_topo)]
		INF = (2 * self.n, -1)
		operation_func = lambda a, b: a if a[0] < b[0] else b
		self.st_rmq = SegmentTree1(2 * self.n - 1, INF, operation_func)
		self.st_rmq.build(depth_list)

	def euler_tour_dfs(self, v, v_par, depth_topo, depth_real):
		self.first_idx[v] = len(self.euler_tour)
		self.euler_tour.append(v)
		self.euler_depth_topo.append(depth_topo)
		self.euler_depth_real.append(depth_real)
		for (v_next, c, d) in self.graph[v]:
			if v_next == v_par:
				continue
			self.euler_tour_dfs(v_next, v, depth_topo + 1, depth_real + d)
			self.euler_tour.append(v)
			self.euler_depth_topo.append(depth_topo)
			self.euler_depth_real.append(depth_real)

	def depth_topo(self, v):
		return self.euler_depth_topo[self.first_idx[v]]

	def depth_real(self, v):
		return self.euler_depth_real[self.first_idx[v]]

	def lca(self, u, v):
		(u_idx, v_idx) = (self.first_idx[u], self.first_idx[v])
		if u_idx > v_idx:
			(u_idx, v_idx) = (v_idx, u_idx)
		(_, idx) = self.st_rmq.query(u_idx, v_idx + 1)
		return self.euler_tour[idx]

	def dist(self, u, v):
		lca_uv = self.lca(u, v)
		return self.depth(u) + self.depth(v) - 2 * self.depth(lca_uv)
(n, q) = map(int, input().split())
graph = [[] for _ in range(n)]
for _ in range(n - 1):
	(a, b, c, d) = map(int, input().split())
	a -= 1
	b -= 1
	graph[a].append((b, c, d))
	graph[b].append((a, c, d))
tree = Tree(n, graph, 0)
query = []
memo = [set() for i in range(n)]
for _ in range(q):
	(x, y, u, v) = map(int, input().split())
	u -= 1
	v -= 1
	lca_uv = tree.lca(u, v)
	memo[u].add(x)
	memo[v].add(x)
	memo[lca_uv].add(x)
	query.append((x, y, u, v, lca_uv))
n_color = [0] * n
d_color = [0] * n
n_color_memo = {}
d_color_memo = {}

def dfs(v, v_par, depth):
	for c in memo[v]:
		n_color_memo[v, c] = n_color[c]
		d_color_memo[v, c] = d_color[c]
	for (v_next, c, d) in graph[v]:
		if v_next == v_par:
			continue
		n_color[c] += 1
		d_color[c] += d
		dfs(v_next, v, depth + d)
		n_color[c] -= 1
		d_color[c] -= d
dfs(0, -1, 0)
for (x, y, u, v, lca) in query:
	depth_u = tree.depth_real(u)
	depth_u += -d_color_memo[u, x] + y * n_color_memo[u, x]
	depth_v = tree.depth_real(v)
	depth_v += -d_color_memo[v, x] + y * n_color_memo[v, x]
	depth_lca = tree.depth_real(lca)
	depth_lca += -d_color_memo[lca, x] + y * n_color_memo[lca, x]
	ans = depth_u + depth_v - 2 * depth_lca
	print(ans)


import sys
sys.setrecursionlimit(2147483647)
INF = float('inf')
MOD = 10 ** 9 + 7
input = lambda : sys.stdin.readline().rstrip()

class LCA(object):

	def __init__(self, E, root=0):
		self.__n = len(E)
		self.__E = E
		self.__logn = (self.__n - 1).bit_length()
		self.__depth = [-1] * self.__n
		self.__dist = [-1] * self.__n
		self.__depth[root] = 0
		self.__dist[root] = 0
		self.__parents = [[-1] * self.__n for _ in range(self.__logn)]
		self.__dfs(root)
		self.__doubling()

	def __dfs(self, v):
		for (u, c, d) in self.__E[v]:
			if self.__depth[u] != -1:
				continue
			self.__parents[0][u] = v
			self.__depth[u] = self.__depth[v] + 1
			self.__dist[u] = self.__dist[v] + d
			self.__dfs(u)

	def __doubling(self):
		for i in range(1, self.__logn):
			for v in range(self.__n):
				if self.__parents[i - 1][v] == -1:
					continue
				self.__parents[i][v] = self.__parents[i - 1][self.__parents[i - 1][v]]

	@property
	def depth(self):
		return self.__depth

	@property
	def dist(self):
		return self.__dist

	def get(self, u, v):
		dd = self.__depth[v] - self.__depth[u]
		if dd < 0:
			(u, v) = (v, u)
			dd *= -1
		for i in range(self.__logn):
			if dd & 1 << i:
				v = self.__parents[i][v]
		if v == u:
			return v
		for i in range(self.__logn - 1, -1, -1):
			(pu, pv) = (self.__parents[i][u], self.__parents[i][v])
			if pu != pv:
				(u, v) = (pu, pv)
		return self.__parents[0][u]

def resolve():
	(n, q) = map(int, input().split())
	E = [[] for _ in range(n)]
	for _ in range(n - 1):
		(a, b, c, d) = map(int, input().split())
		a -= 1
		b -= 1
		E[a].append((b, c, d))
		E[b].append((a, c, d))
	G = LCA(E)
	Qs = [[] for _ in range(n)]
	for i in range(q):
		(x, y, u, v) = map(int, input().split())
		u -= 1
		v -= 1
		c = G.get(u, v)
		Qs[u].append((i, x, y, 1))
		Qs[v].append((i, x, y, 1))
		Qs[c].append((i, x, y, -2))
	ans = [0] * q
	S = [[0, 0] for _ in range(n)]

	def dfs(v, p):
		for (idx, col, mag, coef) in Qs[v]:
			x = G.dist[v]
			x -= S[col][1]
			x += mag * S[col][0]
			ans[idx] += x * coef
		for (nv, col, d) in E[v]:
			if nv == p:
				continue
			S[col][0] += 1
			S[col][1] += d
			dfs(nv, v)
			S[col][0] -= 1
			S[col][1] -= d
	dfs(0, -1)
	print(*ans, sep='\n')
resolve()


import sys, bisect, string, math, time, functools, random
from heapq import heappush, heappop, heapify
from collections import deque, defaultdict, Counter
from itertools import permutations, combinations, groupby

def Golf():
	(*a,) = map(int, open(0))

def I():
	return int(input())

def S_():
	return input()

def IS():
	return input().split()

def LS():
	return [i for i in input().split()]

def LI():
	return [int(i) for i in input().split()]

def LI_():
	return [int(i) - 1 for i in input().split()]

def NI(n):
	return [int(input()) for i in range(n)]

def NI_(n):
	return [int(input()) - 1 for i in range(n)]

def StoLI():
	return [ord(i) - 97 for i in input()]

def ItoS(n):
	return chr(n + 97)

def LtoS(ls):
	return ''.join([chr(i + 97) for i in ls])

def GI(V, E, ls=None, Directed=False, index=1):
	org_inp = []
	g = [[] for i in range(V)]
	FromStdin = True if ls == None else False
	for i in range(E):
		if FromStdin:
			inp = LI()
			org_inp.append(inp)
		else:
			inp = ls[i]
		if len(inp) == 2:
			(a, b) = inp
			c = 1
		else:
			(a, b, c) = inp
		if index == 1:
			a -= 1
			b -= 1
		aa = (a, c)
		bb = (b, c)
		g[a].append(bb)
		if not Directed:
			g[b].append(aa)
	return (g, org_inp)

def GGI(h, w, search=None, replacement_of_found='.', mp_def={'#': 1, '.': 0}, boundary=1):
	mp = [boundary] * (w + 2)
	found = {}
	for i in range(h):
		s = input()
		for char in search:
			if char in s:
				found[char] = (i + 1) * (w + 2) + s.index(char) + 1
				mp_def[char] = mp_def[replacement_of_found]
		mp += [boundary] + [mp_def[j] for j in s] + [boundary]
	mp += [boundary] * (w + 2)
	return (h + 2, w + 2, mp, found)

def TI(n):
	return GI(n, n - 1)

def bit_combination(k, n=2):
	rt = []
	for tb in range(n ** k):
		s = [tb // n ** bt % n for bt in range(k)]
		rt += [s]
	return rt

def show(*inp, end='\n'):
	if show_flg:
		print(*inp, end=end)
YN = ['YES', 'NO']
Yn = ['Yes', 'No']
mo = 10 ** 9 + 7
inf = float('inf')
l_alp = string.ascii_lowercase
input = lambda : sys.stdin.readline().rstrip()

class Tree:

	def __init__(self, inp_size=None, init=True):
		self.LCA_init_stat = False
		self.ETtable = []
		if init:
			self.stdin(inp_size)
		return

	def stdin(self, inp_size=None, index=1):
		if inp_size == None:
			self.size = int(input())
		else:
			self.size = inp_size
		(self.edges, _) = GI(self.size, self.size - 1, index=index)
		return

	def listin(self, ls, index=0):
		self.size = len(ls) + 1
		(self.edges, _) = GI(self.size, self.size - 1, ls, index=index)
		return

	def __str__(self):
		return str(self.edges)

	def dfs(self, x, func=lambda prv, nx, dist: prv + dist, root_v=0):
		q = deque()
		q.append(x)
		v = [-1] * self.size
		v[x] = root_v
		while q:
			c = q.pop()
			for (nb, d) in self.edges[c]:
				if v[nb] == -1:
					q.append(nb)
					v[nb] = func(v[c], nb, d)
		return v

	def EulerTour(self, x):
		q = deque()
		q.append(x)
		self.depth = [None] * self.size
		self.depth[x] = 0
		self.ETtable = []
		self.ETdepth = []
		self.ETin = [-1] * self.size
		self.ETout = [-1] * self.size
		cnt = 0
		while q:
			c = q.pop()
			if c < 0:
				ce = ~c
			else:
				ce = c
				for (nb, d) in self.edges[ce]:
					if self.depth[nb] == None:
						q.append(~ce)
						q.append(nb)
						self.depth[nb] = self.depth[ce] + 1
			self.ETtable.append(ce)
			self.ETdepth.append(self.depth[ce])
			if self.ETin[ce] == -1:
				self.ETin[ce] = cnt
			else:
				self.ETout[ce] = cnt
			cnt += 1
		return

	def LCA_init(self, root):
		self.EulerTour(root)
		self.st = SparseTable(self.ETdepth, init_func=min, init_idl=inf)
		self.LCA_init_stat = True
		return

	def LCA(self, root, x, y):
		if self.LCA_init_stat == False:
			self.LCA_init(root)
		a = self.ETin[x]
		b = self.ETin[y]
		if a > b:
			(a, b) = (b, a)
		id_of_min_dep_in_et = self.st.query_id(a, b + 1)
		return self.ETtable[id_of_min_dep_in_et]

class SparseTable:

	def __init__(self, ls, init_func=min, init_idl=float('inf')):
		self.func = init_func
		self.idl = init_idl
		self.size = len(ls)
		self.N0 = self.size.bit_length()
		self.table = [ls[:]]
		self.index = [list(range(self.size))]
		self.lg = [0] * (self.size + 1)
		for i in range(2, self.size + 1):
			self.lg[i] = self.lg[i >> 1] + 1
		for i in range(self.N0):
			tmp = [self.func(self.table[i][j], self.table[i][min(j + (1 << i), self.size - 1)]) for j in range(self.size)]
			tmp_id = [self.index[i][j] if self.table[i][j] == self.func(self.table[i][j], self.table[i][min(j + (1 << i), self.size - 1)]) else self.index[i][min(j + (1 << i), self.size - 1)] for j in range(self.size)]
			self.table += [tmp]
			self.index += [tmp_id]

	def query(self, l, r):
		N = self.lg[r - l]
		return self.func(self.table[N][l], self.table[N][r - (1 << N)])

	def query_id(self, l, r):
		N = self.lg[r - l]
		(a, b) = (self.index[N][l], self.index[N][r - (1 << N)])
		if self.table[0][a] == self.func(self.table[N][l], self.table[N][r - (1 << N)]):
			b = a
		return b

	def __str__(self):
		return str(self.table[0])

	def print(self):
		for i in self.table:
			print(*i)
show_flg = False
show_flg = True
ans = 0
(n, q) = LI()
ls = deque()
col = dict()
e_len = dict()
for i in range(n - 1):
	(a, b, c, d) = LI_()
	d += 1
	ls += [(a, b, d)]
	eid = a * n + b if a < b else b * n + a
	col[eid] = c
	e_len[eid] = d
tr = Tree(init=False)
tr.listin(ls, index=0)
root = 0
dist = tr.dfs(root)
tr.EulerTour(0)
dep = tr.depth
et = tr.ETtable
ad = [[0] for i in range(n)]
an = [[0] for i in range(n)]
index = [[] for i in range(n)]
for i in range(2 * n - 1 - 1):
	(a, b) = (et[i], et[i + 1])
	sign = 1 if dep[a] < dep[b] else -1
	eid = a * n + b if a < b else b * n + a
	c = col[eid]
	dd = e_len[eid] * sign
	an[c] += [an[c][-1] + sign]
	ad[c] += [ad[c][-1] + dd]
	index[c] += [i]
for i in range(n):
	ad[i] += [ad[i][-1]]
	an[i] += [an[i][-1]]
for i in range(q):
	(x, y, u, v) = LI_()
	y += 1
	lca = tr.LCA(root, u, v)
	path = dist[u] + dist[v] - dist[lca] * 2
	ui = bisect.bisect_left(index[x], tr.ETin[u])
	vi = bisect.bisect_left(index[x], tr.ETin[v])
	ai = bisect.bisect_left(index[x], tr.ETin[lca])
	c_path = ad[x][ui] + ad[x][vi] - ad[x][ai] * 2
	c_n = an[x][ui] + an[x][vi] - an[x][ai] * 2
	ans = path - c_path + c_n * y
	print(ans)


import sys
from itertools import combinations, permutations, product, combinations_with_replacement, accumulate
from heapq import heapify, heappop, heappush, heappushpop
from bisect import bisect_left, bisect_right
from collections import Counter, defaultdict, deque
from math import sqrt, log, floor, ceil, factorial, cos, sin, pi
from fractions import gcd
from operator import mul
from functools import reduce
sys.setrecursionlimit(10 ** 8)
input = sys.stdin.readline
INF = float('inf')
LINF = 2 ** 63 - 1
NIL = -LINF
MOD = 10 ** 9 + 7
MGN = 4

def AST(exp: bool, msg: str=''):
	assert exp, msg

def TAST(exp: bool, msg=''):
	if exp is False:
		print('TAssertionError:', msg)
	while exp is False:
		pass

def EPR(msg):
	print(msg, file=sys.stderr)

def II():
	return int(input())

def IF():
	return float(input())

def IS():
	return input().replace('\n', '')

def ILCI(n: int):
	return [II() for _ in range(n)]

def ILCF(n: int):
	return [IF() for _ in range(n)]

def ILI():
	return list(map(int, input().split()))

def ILLI(n: int):
	return [[int(j) for j in input().split()] for i in range(n)]

def ILF():
	return list(map(float, input().split()))

def ILLF(n: int):
	return [[float(j) for j in input().split()] for i in range(n)]

def LTOS(lst: list, sep: str=' '):
	return sep.join(map(str, lst))

def DEC(lst: list):
	return list(map(lambda x: x - 1, lst))

def INC(lst: list):
	return list(map(lambda x: x + 1, lst))

class Queue:

	def __init__(self) -> None:
		self.items = deque()

	def is_empty(self) -> bool:
		return len(self.items) == 0

	def enqueue(self, item) -> None:
		self.items.appendleft(item)

	def insert(self, item) -> None:
		self.enqueue(item)

	def dequeue(self):
		return self.items.pop()

	def front(self):
		return self.items[-1]

	def pop(self) -> None:
		self.items.pop()

	def size(self) -> int:
		return len(self.items)

class LCA:

	def __init__(self, N: int) -> None:
		self.N = N
		self.to = [[] for _ in range(N)]
		self.co = [[] for _ in range(N)]
		self.dep = [0] * N
		self.costs = [0] * N
		l = 0
		while 1 << l < N:
			l += 1
		self.l = l
		self.par = [[0] * l for _ in range(N + 1)]

	def add_edge(self, a: int, b: int, c=0) -> None:
		self.to[a].append(b)
		self.co[a].append(c)
		self.to[b].append(a)
		self.co[b].append(c)

	def _dfs(self, v: int, d: int=0, c=0, p: int=-1) -> None:
		if p != -1:
			self.par[v][0] = p
		self.dep[v] = d
		self.costs[v] = c
		for i in range(len(self.to[v])):
			u = self.to[v][i]
			if u == p:
				continue
			else:
				self._dfs(u, d + 1, c + self.co[v][i], v)

	def _bfs(self, root: int) -> None:
		que = Queue()
		que.enqueue(root)
		self.dep[root] = 0
		self.costs[root] = 0
		vis = [False] * self.N
		while que.is_empty() is False:
			v = que.dequeue()
			vis[v] = True
			nd = self.dep[v] + 1
			cs = self.costs[v]
			for i in range(len(self.to[v])):
				nv = self.to[v][i]
				if vis[nv]:
					continue
				else:
					que.enqueue(nv)
					self.dep[nv] = nd
					self.costs[nv] = cs + self.co[v][i]
					self.par[nv][0] = v

	def init(self, root: int=0) -> None:
		self.root = root
		self._bfs(root)
		for i in range(self.l - 1):
			for v in range(self.N):
				self.par[v][i + 1] = self.par[self.par[v][i]][i]

	def lca(self, a: int, b: int) -> int:
		(dep_s, dep_l) = (self.dep[a], self.dep[b])
		if dep_s > dep_l:
			(a, b) = (b, a)
			(dep_s, dep_l) = (dep_l, dep_s)
		gap = dep_l - dep_s
		L_1 = self.l - 1
		par = self.par
		for i in range(L_1, -1, -1):
			leng = 1 << i
			if gap >= leng:
				gap -= leng
				b = par[b][i]
		if a == b:
			return a
		for i in range(L_1, -1, -1):
			na = par[a][i]
			nb = par[b][i]
			if na != nb:
				(a, b) = (na, nb)
		return par[a][0]

	def length(self, a: int, b: int) -> int:
		c = self.lca(a, b)
		dep = self.dep
		return dep[a] + dep[b] - dep[c] * 2

	def dist(self, a: int, b: int):
		c = self.lca(a, b)
		costs = self.costs
		return costs[a] + costs[b] - costs[c] * 2

def main():
	(N, Q) = ILI()
	gr = LCA(N)
	es = [[] for _ in range(N)]
	for i in range(N - 1):
		(a, b, col, dist) = ILI()
		a -= 1
		b -= 1
		es[a].append((b, dist, col))
		es[b].append((a, dist, col))
		gr.add_edge(a, b, dist)
	gr.init()
	ans = [0] * Q
	qs = [[] for _ in range(N)]
	for i in range(Q):
		(cx, dy, a, b) = ILI()
		a -= 1
		b -= 1
		c = gr.lca(a, b)
		ans[i] = gr.costs[a] + gr.costs[b] - gr.costs[c] * 2
		qs[a].append((cx, i, 1, dy))
		qs[b].append((cx, i, 1, dy))
		qs[c].append((cx, i, -2, dy))
	cnt = [0] * N
	sum_ = [0] * N

	def dfs(v: int, p: int=-1) -> None:
		for (col, qid, coeff, dist) in qs[v]:
			x = -sum_[col]
			x += dist * cnt[col]
			ans[qid] += x * coeff
		for (to, co, col) in es[v]:
			if to == p:
				continue
			cnt[col] += 1
			sum_[col] += co
			dfs(to, v)
			cnt[col] -= 1
			sum_[col] -= co
	dfs(0)
	print(*ans, sep='\n')
main()


from collections import defaultdict
from math import log2
import sys
sys.setrecursionlimit(10 ** 6)
input = sys.stdin.readline

def f(n, q):

	def dfs(i, oi=-1, dpt=0):
		depth[i] = dpt
		parent_2k[0][i] = oi
		for (ki, c, d) in to[i]:
			if ki == oi:
				continue
			dfs(ki, i, dpt + 1)
		return

	def make_parent(level):
		parent_2kk1 = parent_2k[0]
		for k in range(1, level):
			parent_2kk = parent_2k[k]
			for i in range(n):
				parent1 = parent_2kk1[i]
				if parent1 != -1:
					parent_2kk[i] = parent_2kk1[parent1]
			parent_2kk1 = parent_2kk
		return

	def lca(u, v):
		dist_uv = depth[u] - depth[v]
		if dist_uv < 0:
			(u, v) = (v, u)
			dist_uv *= -1
		k = 0
		while dist_uv:
			if dist_uv & 1:
				u = parent_2k[k][u]
			dist_uv >>= 1
			k += 1
		if u == v:
			return u
		for k in range(int(log2(depth[u])) + 1, -1, -1):
			pu = parent_2k[k][u]
			pv = parent_2k[k][v]
			if pu != pv:
				u = pu
				v = pv
		return parent_2k[0][u]
	to = defaultdict(list)
	root = 0
	depth = [0] * n
	level = int(log2(n)) + 1
	parent_2k = [[-1] * n for _ in range(level)]
	for _ in range(n - 1):
		(a, b, c, d) = list(map(int, input().split()))
		to[a - 1] += [[b - 1, c - 1, d]]
		to[b - 1] += [[a - 1, c - 1, d]]
	dfs(root)
	make_parent(level)
	qs = [[] for _ in range(n)]
	for qi in range(q):
		(x, y, u, v) = map(int, input().split())
		qs[u - 1].append([qi, x - 1, y, 1])
		qs[v - 1].append([qi, x - 1, y, 1])
		qs[lca(u - 1, v - 1)].append([qi, x - 1, y, -2])
	cnt_c = [0] * n
	sum_c = [0] * n
	ans = [0] * q

	def dfs2(i, oi=-1, sum_d=0):
		for (qi, qc, qd, qk) in qs[i]:
			ans[qi] += (sum_d - sum_c[qc] + qd * cnt_c[qc]) * qk
		for (ki, c, d) in to[i]:
			if ki == oi:
				continue
			cnt_c[c] += 1
			sum_c[c] += d
			dfs2(ki, i, sum_d + d)
			cnt_c[c] -= 1
			sum_c[c] -= d
		return
	dfs2(root)
	for x in ans:
		print(x)
(n, q) = map(int, input().split())
f(n, q)


import sys
input = sys.stdin.readline
sys.setrecursionlimit(10 ** 7)
(N, Q) = list(map(int, input().split()))
graph = [[] for _ in range(N + 1)]
for _ in range(N - 1):
	(a, b, c, d) = list(map(int, input().split()))
	graph[a].append((b, c, d))
	graph[b].append((a, c, d))
query = [[int(x) for x in input().split()] for _ in range(Q)]
depth = [0] * (N + 1)
U = 17
ancestor = [[0] * (U + 1) for _ in range(N + 1)]
q = [(1, 0, 0)]
while q:
	qq = []
	for (x, d, parent) in q:
		depth[x] = d
		ax = ancestor[x]
		ax[0] = parent
		for i in range(d.bit_length() - 1):
			ax[i + 1] = ancestor[ax[i]][i]
		for (y, _, _) in graph[x]:
			if y == parent:
				continue
			qq.append((y, d + 1, x))
	q = qq

def LCA(x, y):
	dx = depth[x]
	dy = depth[y]
	if dx > dy:
		(x, y) = (y, x)
		(dx, dy) = (dy, dx)
	diff = dy - dx
	while diff:
		step = diff & -diff
		y = ancestor[y][step.bit_length() - 1]
		diff -= step
	while x != y:
		j = 0
		while ancestor[x][j] != ancestor[y][j]:
			j += 1
		if j == 0:
			return ancestor[x][0]
		x = ancestor[x][j - 1]
		y = ancestor[y][j - 1]
	return x
tasks = [[] for _ in range(N + 1)]
for (i, (x, y, u, v)) in enumerate(query):
	tasks[u].append((i, x, y, 1))
	tasks[v].append((i, x, y, 1))
	tasks[LCA(u, v)].append((i, x, y, -2))
answer = [0] * Q

def dfs(x=1, sums=[0] * (N + 1), nums=[0] * (N + 1), total=0, parent=0):
	for (i, c, d, coef) in tasks[x]:
		answer[i] += coef * (total - sums[c] + nums[c] * d)
	for (y, c, d) in graph[x]:
		if y == parent:
			continue
		sums[c] += d
		nums[c] += 1
		total += d
		dfs(y, sums, nums, total, x)
		sums[c] -= d
		nums[c] -= 1
		total -= d
	return
dfs()
print('\n'.join(map(str, answer)))


import sys
from copy import deepcopy
from operator import add, itemgetter

class Segtree:

	def __init__(self, A, intv, initialize=True, segf=max):
		self.N = len(A)
		self.N0 = 2 ** (self.N - 1).bit_length()
		self.intv = intv
		self.segf = segf
		if initialize:
			self.data = [intv] * self.N0 + A + [intv] * (self.N0 - self.N)
			for i in range(self.N0 - 1, 0, -1):
				self.data[i] = self.segf(self.data[2 * i], self.data[2 * i + 1])
		else:
			self.data = [intv] * (2 * self.N0)

	def update(self, k, x):
		k += self.N0
		self.data[k] = x
		while k > 0:
			k = k >> 1
			self.data[k] = self.segf(self.data[2 * k], self.data[2 * k + 1])

	def query(self, l, r):
		(L, R) = (l + self.N0, r + self.N0)
		s = self.intv
		while L < R:
			if R & 1:
				R -= 1
				s = self.segf(s, self.data[R])
			if L & 1:
				s = self.segf(s, self.data[L])
				L += 1
			L >>= 1
			R >>= 1
		return s

def getpar(Edge, p):
	N = len(Edge)
	par = [0] * N
	par[p] = None
	stack = [p]
	visited = set([p])
	while stack:
		vn = stack.pop()
		for vf in Edge[vn]:
			if vf in visited:
				continue
			visited.add(vf)
			par[vf] = vn
			stack.append(vf)
	return par

def getcld(p):
	res = [[] for _ in range(len(p))]
	for (i, v) in enumerate(p[1:], 1):
		res[v].append(i)
	return res

def eulertour(Par, Cld, st):
	Cld = deepcopy(Cld)
	N = len(Cld)
	St = [None] * N
	En = [None] * N
	et = []
	sign = []
	vf = st
	cnt = 0
	depth = [0] * N
	while vf is not None:
		vn = vf
		et.append(vn)
		if St[vn] is None:
			St[vn] = cnt
		En[vn] = cnt
		cnt += 1
		if Cld[vn]:
			vf = Cld[vn].pop()
			sign.append(1)
			depth[vf] = depth[vn] + 1
		else:
			vf = P[vn]
			sign.append(-1)
	return (St, En, et, sign[:-1], depth)
(N, Q) = map(int, input().split())
Edge = [[] for _ in range(N)]
Edgelen = [0] * (N - 1)
Edgenum = {}
Color = [[] for _ in range(N)]
for i in range(N - 1):
	(a, b, c, d) = map(int, sys.stdin.readline().split())
	a -= 1
	b -= 1
	Edge[a].append(b)
	Edge[b].append(a)
	Edgelen[i] = d
	Edgenum[a, b] = i
	Edgenum[b, a] = i
	Color[c].append(i)
P = getpar(Edge, 0)
C = getcld(P)
(St, En, et, sign, depth) = eulertour(P, C, 0)
etnum = [Edgenum[v1, v2] for (v1, v2) in zip(et, et[1:])]
etnuminv = [[] for _ in range(N - 1)]
for (i, e) in enumerate(etnum):
	etnuminv[e].append(i)
etl = [Edgelen[num] * sign[i] for (i, num) in enumerate(etnum)]
LCA = Segtree(et, None, initialize=True, segf=lambda x, y: x if y is None or (x is not None and depth[x] < depth[y]) else y)
Leng = Segtree([0] + etl, 0, initialize=True, segf=add)
Query = [tuple(map(int, sys.stdin.readline().split())) for _ in range(Q)]
Ans = [None] * Q
CQ = [[] for _ in range(N)]
for (i, (x, y, u, v)) in enumerate(Query):
	CQ[x].append((i, y, u - 1, v - 1))
for (col, cq) in enumerate(CQ):
	if not cq:
		continue
	inf = 2 * 10 ** 9
	for vidx in Color[col]:
		Leng.update(1 + etnuminv[vidx][0], inf)
		Leng.update(1 + etnuminv[vidx][1], -inf)
	for (i, y, u, v) in cq:
		if St[u] > St[v]:
			(u, v) = (v, u)
		su = St[u]
		sv = St[v]
		lca = LCA.query(su, sv + 1)
		dis = Leng.query(0, su + 1) + Leng.query(0, sv + 1) - 2 * Leng.query(0, St[lca] + 1)
		(disq, disr) = divmod(dis, inf)
		Ans[i] = disr + disq * y
	for vidx in Color[col]:
		Leng.update(1 + etnuminv[vidx][0], Edgelen[vidx])
		Leng.update(1 + etnuminv[vidx][1], -Edgelen[vidx])
print('\n'.join(map(str, Ans)))


import sys
sys.setrecursionlimit(1000000)
input = sys.stdin.readline
from bisect import bisect_right as br
(N, K) = map(int, input().split())
X = [[] for i in range(N)]
for i in range(N - 1):
	(x, y, c, d) = map(int, input().split())
	X[x - 1].append((y - 1, c - 1, d))
	X[y - 1].append((x - 1, c - 1, d))
P = [(-1, 0, 0)] * N
Q = [(0, 0, 0)]
R = []
ET = []
ct = 0
CT = [0] * (N - 1) * 2
CTA = [-1] * N
BT = [[[-1], [0], [0]] for _ in range(N)]
while Q:
	(i, c, d) = Q.pop()
	if i < 0:
		BT[c][0].append(ct)
		BT[c][1].append(BT[c][1][-1] - 1)
		BT[c][2].append(BT[c][2][-1] - d)
		ct += 1
		continue
	R.append(i)
	if i > 0:
		if CTA[i] < 0:
			CTA[i] = ct
		BT[c][0].append(ct)
		BT[c][1].append(BT[c][1][-1] + 1)
		BT[c][2].append(BT[c][2][-1] + d)
		ct += 1
	for (a, c, d) in X[i][::-1]:
		if a != P[i][0]:
			ET.append((i, a))
			P[a] = (i, c, d)
			for k in range(len(X[a])):
				if X[a][k][0] == i:
					del X[a][k]
					break
			Q.append((~a, c, d))
			Q.append((a, c, d))
L = [[0, 0] for _ in range(N)]
for i in R[1:]:
	(p, c, d) = P[i]
	L[i][0] = L[p][0] + 1
	L[i][1] = L[p][1] + d
D = [[P[i][0] for i in range(N)]] + [[-1] * N for _ in range(16)]
for k in range(1, 17):
	for i in range(N):
		a = D[k - 1][i]
		if a > 0:
			D[k][i] = D[k - 1][a]

def lca(u0, v0):
	(u, v) = (u0, v0)
	if L[u][0] < L[v][0]:
		(u, v) = (v, u)
	while L[u][0] > L[v][0]:
		d = L[u][0] - L[v][0]
		dd = d.bit_length() - 1
		u = D[dd][u]
	if u == v:
		return u
	for i in range(17)[::-1]:
		if D[i][u] != D[i][v]:
			u = D[i][u]
			v = D[i][v]
	return D[0][u]

def calc(x, y, w):
	aa = br(BT[x][0], CTA[w]) - 1
	c = BT[x][1][aa]
	d = BT[x][2][aa]
	return L[w][1] + c * y - d
for _ in range(K):
	(x, y, u, v) = map(int, input().split())
	(x, u, v) = (x - 1, u - 1, v - 1)
	ll = lca(u, v)
	print(calc(x, y, u) + calc(x, y, v) - 2 * calc(x, y, ll))


from collections import defaultdict, deque
from heapq import heappush, heappop
import sys
import math
import bisect
import random

def LI():
	return [int(x) for x in sys.stdin.readline().split()]

def I():
	return int(sys.stdin.readline())

def LS():
	return [list(x) for x in sys.stdin.readline().split()]

def S():
	return list(sys.stdin.readline())[:-1]

def IR(n):
	return [I() for i in range(n)]

def LIR(n):
	return [LI() for i in range(n)]

def SR(n):
	return [S() for i in range(n)]

def LSR(n):
	return [LS() for i in range(n)]
sys.setrecursionlimit(1000000)
mod = 1000000007

def A():
	(n, a, b) = LI()
	print(min(a * n, b))
	return

def B():
	(n, d) = LI()
	x = LIR(n)
	f = defaultdict(lambda : 0)
	for i in range(1000000):
		f[i * i] = 1
	ans = 0
	for i in range(n):
		for j in range(i + 1, n):
			s = 0
			for k in range(d):
				s += (x[i][k] - x[j][k]) ** 2
			if f[s]:
				ans += 1
	print(ans)
	return

def C():
	(l, r) = LI()
	if r - l > 2019:
		print(0)
		quit()
	ans = float('inf')
	for i in range(l, r + 1):
		for j in range(i + 1, r + 1):
			if i * j % 2019 < ans:
				ans = i * j % 2019
	print(ans)
	return

def D():
	n = I()
	a = LI()
	for i in range(n):
		a[i] *= 2
	x = a[0]
	for i in range(1, n):
		if i % 2:
			x -= a[i]
		else:
			x += a[i]
	b = [x // 2]
	for i in range(n - 1):
		b.append(abs(b[-1] - a[i]))
	print(*b)
	return

def E():
	(n, k) = LI()
	v = [[] for i in range(n)]
	for i in range(n - 1):
		(a, b) = LI()
		a -= 1
		b -= 1
		v[a].append(b)
		v[b].append(a)
	if k == 1:
		if n == 1:
			print(1)
			quit()
		else:
			print(0)
			quit()
	if k == 2:
		print(2)
		quit()
	d = [0] * n
	v.append([])
	v.append([])
	q = deque([0])
	bfs = [1] * n
	bfs[0] = 0
	f = [0] * n
	while q:
		x = q.popleft()
		for y in v[x]:
			if bfs[y]:
				bfs[y] = 0
				q.append(y)
	ans = 1
	q = deque([0])
	dp = [None] * n
	dp[0] = k
	bfs = [1] * n
	bfs[0] = 0
	while q:
		x = q.popleft()
		m = len(v[x])
		for y in v[x]:
			if bfs[y]:
				bfs[y] = 0
				dp[y] = k - m
				m -= 1
				q.append(y)
	for i in dp:
		ans *= i
		ans %= mod
	print(ans)
	return

def F():

	def lca(x, y):
		k = depth[x] - depth[y]
		while k > 0:
			x = par[x][int(math.log(k, 2))]
			k = depth[x] - depth[y]
		k = -k
		while k > 0:
			y = par[y][int(math.log(k, 2))]
			k = depth[y] - depth[x]
		if x == y:
			return x
		for i in range(M + 1)[::-1]:
			if i < len(par[x]):
				if par[x][i] != par[y][i]:
					x = par[x][i]
					y = par[y][i]
		return par[x][0]

	def euler_tour(x, check, color):
		for (i, c, d, k) in f[x]:
			if k:
				query[i] -= 2 * (-color[c][0] + color[c][1] * d)
			else:
				query[i] += -color[c][0] + color[c][1] * d
		for (y, c, d) in v[x]:
			if not check[y]:
				check[y] = 1
				color[c][0] += d
				color[c][1] += 1
				euler_tour(y, check, color)
				check[y] = 0
				color[c][0] -= d
				color[c][1] -= 1
	M = 16
	(n, Q) = LI()
	v = [[] for i in range(n)]
	for i in range(n - 1):
		(a, b, c, d) = LI()
		a -= 1
		b -= 1
		c -= 1
		v[a].append((b, c, d))
		v[b].append((a, c, d))
	par = [[] for i in range(n)]
	par[0].append(0)
	dist = [-1] * n
	dist[0] = 0
	depth = [0] * n
	q = deque([0])
	while q:
		x = q.popleft()
		for (y, c, d) in v[x]:
			if dist[y] < 0:
				dist[y] = dist[x] + d
				depth[y] = depth[x] + 1
				q.append(y)
				par[y].append(x)
	l = [i for i in range(n)]
	l.sort(key=lambda x: depth[x])
	for i in range(M):
		k = 1 << i + 1
		for x in l[::-1]:
			if depth[x] < k:
				break
			par[x].append(par[par[x][i]][i])
	f = [[] for i in range(n)]
	query = [0] * Q
	for i in range(Q):
		(x, y, a, b) = LI()
		x -= 1
		a -= 1
		b -= 1
		f[a].append((i, x, y, 0))
		f[b].append((i, x, y, 0))
		l = lca(a, b)
		f[l].append((i, x, y, 1))
		query[i] = dist[a] + dist[b] - 2 * dist[l]
	check = [0] * n
	check[0] = 1
	color = [[0, 0] for i in range(n)]
	euler_tour(0, check, color)
	for i in query:
		print(i)
	return
F()


import sys
input = sys.stdin.buffer.readline
sys.setrecursionlimit(10 ** 7)
(N, Q) = map(int, input().split())
D = N.bit_length()
edge = [[] for _ in range(N)]
for _ in range(N - 1):
	(a, b, c, d) = map(int, input().split())
	a -= 1
	b -= 1
	edge[a].append((b, c, d))
	edge[b].append((a, c, d))
query = tuple((tuple(map(int, input().split())) for _ in range(Q)))
par = [[-1] * N for _ in range(D)]
dep = [0] * N
dist = [0] * N
node = [0]
while node:
	s = node.pop()
	for (t, _, d) in edge[s]:
		if par[0][s] == t:
			continue
		par[0][t] = s
		dep[t] = dep[s] + 1
		dist[t] = dist[s] + d
		node.append(t)
for d in range(D - 1):
	for i in range(N):
		par[d + 1][i] = par[d][par[d][i]]

def LCA(x, y):
	if dep[x] < dep[y]:
		(x, y) = (y, x)
	h = dep[x] - dep[y]
	for d in reversed(range(D)):
		if h >= 1 << d:
			x = par[d][x]
			h -= 1 << d
	if x == y:
		return x
	for d in reversed(range(D)):
		if par[d][x] != par[d][y]:
			x = par[d][x]
			y = par[d][y]
	return par[0][x]
memo = [[] for _ in range(N)]
ans = [0] * Q
for (i, (x, y, u, v)) in enumerate(query):
	u -= 1
	v -= 1
	c = LCA(u, v)
	ans[i] = dist[u] + dist[v] - 2 * dist[c]
	memo[u].append((x, y, i))
	memo[v].append((x, y, i))
	memo[c].append((x, y, ~i))
c_len = [0] * (N + 1)
c_cnt = [0] * (N + 1)

def dfs(s, p=-1):
	for (c, val, i) in memo[s]:
		if i >= 0:
			ans[i] -= c_len[c]
			ans[i] += c_cnt[c] * val
		else:
			ans[~i] += c_len[c] * 2
			ans[~i] -= c_cnt[c] * val * 2
	for (t, c, d) in edge[s]:
		if t == p:
			continue
		c_len[c] += d
		c_cnt[c] += 1
		dfs(t, s)
		c_len[c] -= d
		c_cnt[c] -= 1
dfs(0)
print(*ans, sep='\n')


import sys
from collections import defaultdict
readline = sys.stdin.readline
(N, Q) = map(int, readline().split())

class SegTree:

	def __init__(self, init_val, n, ide_ele, seg_func):
		self.segfunc = seg_func
		self.num = 2 ** (n - 1).bit_length()
		self.ide_ele = ide_ele
		self.seg = [self.ide_ele] * 2 * self.num
		for i in range(n):
			self.seg[i + self.num - 1] = init_val[i]
		for i in range(self.num - 2, -1, -1):
			self.seg[i] = self.segfunc(self.seg[2 * i + 1], self.seg[2 * i + 2])

	def update(self, k, x):
		k += self.num - 1
		self.seg[k] = x
		while k + 1:
			k = (k - 1) // 2
			self.seg[k] = self.segfunc(self.seg[k * 2 + 1], self.seg[k * 2 + 2])

	def query(self, p, q):
		if q <= p:
			return self.ide_ele
		p += self.num - 1
		q += self.num - 2
		res = self.ide_ele
		while q - p > 1:
			if p & 1 == 0:
				res = self.segfunc(res, self.seg[p])
			if q & 1 == 1:
				res = self.segfunc(res, self.seg[q])
				q -= 1
			p = p // 2
			q = (q - 1) // 2
		if p == q:
			res = self.segfunc(res, self.seg[p])
		else:
			res = self.segfunc(self.segfunc(res, self.seg[p]), self.seg[q])
		return res

def bsearch(ll, target, min_i, max_i):
	if ll[max_i] <= target:
		return max_i
	if target < ll[min_i]:
		return None
	index = len(ll) // 2
	while True:
		if ll[index] <= target:
			if target < ll[index + 1]:
				return index
			(index, min_i) = ((index + 1 + max_i) // 2, index + 1)
			continue
		(index, max_i) = ((index - 1 + min_i) // 2, index - 1)

def process():
	G = [[] for _ in range(N + 1)]
	dp = {}
	for v in range(N - 1):
		(a, b, c, d) = map(int, readline().split())
		G[a].append(b)
		G[b].append(a)
		dp[min(a, b), max(a, b)] = (c, d)
	S = [1] * (2 * N + 1)
	F = [0] * (N + 1)
	stack = []
	for u in G[1]:
		stack += [-1, u]
	visited = set([1])
	(depth, depthh) = ([0] * (N + 1), [0] * (N + 1))
	mmn = [[0] for _ in range(N)]
	msn = [[0] for _ in range(N)]
	iin = [[0] for _ in range(N)]
	path = [1]
	ii = 0
	while True:
		if len(stack) == 0:
			break
		v = stack.pop()
		ii += 1
		if v > 0:
			visited.add(v)
			parent = path[-1]
			path.append(v)
			(cc, dpd) = dp[min(v, parent), max(v, parent)]
			(F[v], S[ii]) = (ii, v)
			(depth[v], depthh[v]) = (depth[parent] + 1, depthh[parent] + dpd)
			mmn[cc].append(mmn[cc][-1] + 1)
			msn[cc].append(msn[cc][-1] + dpd)
			iin[cc].append(ii)
			for u in G[v]:
				if u in visited:
					continue
				stack += [-v, u]
		else:
			child = path.pop()
			(cc, dpd) = dp[min(-v, child), max(-v, child)]
			S[ii] = v
			mmn[cc].append(mmn[cc][-1] - 1)
			iin[cc].append(ii)
			msn[cc].append(msn[cc][-1] - dpd)
	stree = SegTree([(depth[abs(v)], i) for (i, v) in enumerate(S)], len(S), (N, None), min)
	for q in range(Q):
		(x, y, u, v) = map(int, readline().split())
		fu = F[u]
		fv = F[v]
		if fu > fv:
			(fu, fv) = (fv, fu)
		cc = abs(S[stree.query(fu, fv + 1)[1]])
		(ll, mm, ms) = (iin[x], mmn[x], msn[x])
		uvcs = []
		for target_i in [F[u], F[v], F[cc]]:
			index = bsearch(ll, target_i, 0, len(ll) - 1)
			uvcs.append((mm[index], ms[index]))
		diff = (uvcs[0][0] + uvcs[1][0] - 2 * uvcs[2][0]) * y - (uvcs[0][1] + uvcs[1][1] - 2 * uvcs[2][1])
		print(depthh[u] + depthh[v] - 2 * depthh[cc] + diff)
process()


from collections import defaultdict, deque, Counter
from heapq import heappush, heappop, heapify
import math
import bisect
import random
from itertools import permutations, accumulate, combinations, product
import sys
from pprint import pprint
from copy import deepcopy
import string
from bisect import bisect_left, bisect_right
from math import factorial, ceil, floor
from operator import mul
from functools import reduce
from pprint import pprint
sys.setrecursionlimit(2147483647)
INF = 10 ** 13

def LI():
	return list(map(int, sys.stdin.buffer.readline().split()))

def I():
	return int(sys.stdin.buffer.readline())

def LS():
	return sys.stdin.buffer.readline().rstrip().decode('utf-8').split()

def S():
	return sys.stdin.buffer.readline().rstrip().decode('utf-8')

def IR(n):
	return [I() for i in range(n)]

def LIR(n):
	return [LI() for i in range(n)]

def SR(n):
	return [S() for i in range(n)]

def LSR(n):
	return [LS() for i in range(n)]

def SRL(n):
	return [list(S()) for i in range(n)]

def MSRL(n):
	return [[int(j) for j in list(S())] for i in range(n)]
mod = 1000000007

class LCA(object):

	def __init__(self, G, root=0):
		self.G = G
		self.root = root
		self.n = len(G)
		self.logn = (self.n - 1).bit_length()
		self.depth = [-1 if i != root else 0 for i in range(self.n)]
		self.parent = [[-1] * self.n for _ in range(self.logn)]
		self.dfs()
		self.doubling()

	def dfs(self):
		que = [self.root]
		while que:
			u = que.pop()
			for v in self.G[u]:
				if self.depth[v] == -1:
					self.depth[v] = self.depth[u] + 1
					self.parent[0][v] = u
					que += [v]

	def doubling(self):
		for i in range(1, self.logn):
			for v in range(self.n):
				if self.parent[i - 1][v] != -1:
					self.parent[i][v] = self.parent[i - 1][self.parent[i - 1][v]]

	def get(self, u, v):
		if self.depth[v] < self.depth[u]:
			(u, v) = (v, u)
		du = self.depth[u]
		dv = self.depth[v]
		for i in range(self.logn):
			if dv - du >> i & 1:
				v = self.parent[i][v]
		if u == v:
			return u
		for i in range(self.logn - 1, -1, -1):
			(pu, pv) = (self.parent[i][u], self.parent[i][v])
			if pu != pv:
				(u, v) = (pu, pv)
		return self.parent[0][u]
(n, q) = LI()
edge_dict = [{} for _ in range(n)]
for (u, v, c, d) in LIR(n - 1):
	edge_dict[u - 1][v - 1] = (c, d)
	edge_dict[v - 1][u - 1] = (c, d)
lca = LCA(edge_dict)
query = [[] for _ in range(n)]
for (q_idx, (x, y, u, v)) in enumerate(LIR(q)):
	query[u - 1] += [(q_idx, x, y, 1)]
	query[v - 1] += [(q_idx, x, y, 1)]
	query[lca.get(u - 1, v - 1)] += [(q_idx, x, y, 0)]

def eulertour(G, root=0):
	n = len(G)
	euler = []
	depth = [-1] * n
	depth[root] = 0
	que = deque([root])
	que2 = deque()
	visited = [0] * n
	while que:
		u = que.pop()
		euler += [(depth[u], u)]
		if visited[u]:
			continue
		for v in G[u]:
			if visited[v]:
				que += [v]
			else:
				depth[v] = depth[u] + 1
				que2 += [v]
		que.extend(que2)
		que2 = deque()
		visited[u] = 1
	return euler
dist = 0
color_len = [0] * n
color_cnt = [0] * n
ans = [0] * q
euler = eulertour(edge_dict)
for i in range(1, len(euler)):
	dep = euler[i][0]
	(c, d) = edge_dict[euler[i - 1][1]][euler[i][1]]
	if euler[i - 1][0] < euler[i][0]:
		dist += d
		color_len[c] += d
		color_cnt[c] += 1
		for (q_idx, x, y, flag) in query[euler[i][1]]:
			if flag:
				ans[q_idx] += dist - color_len[x] + color_cnt[x] * y
			else:
				ans[q_idx] -= (dist - color_len[x] + color_cnt[x] * y) * 2
	else:
		dist -= d
		color_len[c] -= d
		color_cnt[c] -= 1
print(*ans, sep='\n')


import sys
sys.setrecursionlimit(10 ** 5 + 5)
from collections import defaultdict, deque
input = sys.stdin.readline

class LCA:

	def __init__(self, n):
		self.size = n + 1
		self.bitlen = n.bit_length()
		self.ancestor = [[0] * self.size for i in range(self.bitlen)]
		self.depth = [-1] * self.size
		self.dis = [-1] * self.size
		self.depth[1] = 0
		self.dis[1] = 0

	def make(self, root):
		q = deque([root])
		while q:
			now = q.popleft()
			for (nex, c, w) in e[now]:
				if self.depth[nex] >= 0:
					continue
				self.depth[nex] = self.depth[now] + 1
				self.dis[nex] = self.dis[now] + w
				self.ancestor[0][nex] = now
				q.append(nex)
		for i in range(1, self.bitlen):
			for j in range(self.size):
				if self.ancestor[i - 1][j] > 0:
					self.ancestor[i][j] = self.ancestor[i - 1][self.ancestor[i - 1][j]]

	def lca(self, x, y):
		dx = self.depth[x]
		dy = self.depth[y]
		if dx < dy:
			(x, y) = (y, x)
			(dx, dy) = (dy, dx)
		dif = dx - dy
		while dif:
			s = dif & -dif
			x = self.ancestor[s.bit_length() - 1][x]
			dif -= s
		while x != y:
			j = 0
			while self.ancestor[j][x] != self.ancestor[j][y]:
				j += 1
			if j == 0:
				return self.ancestor[0][x]
			x = self.ancestor[j - 1][x]
			y = self.ancestor[j - 1][y]
		return x
(n, q) = map(int, input().split())
e = [[] for i in range(n + 1)]
for i in range(n - 1):
	(a, b, c, d) = map(int, input().split())
	e[a].append((b, c, d))
	e[b].append((a, c, d))
lca = LCA(n)
lca.make(1)
Q = [[] for i in range(n + 1)]
ans = []
for i in range(q):
	(x, y, u, v) = map(int, input().split())
	a = lca.lca(u, v)
	ans.append(lca.dis[u] + lca.dis[v] - 2 * lca.dis[a])
	Q[u].append((x, y, i, 1))
	Q[v].append((x, y, i, 1))
	Q[a].append((x, y, i, -2))
count = defaultdict(int)
weight = defaultdict(int)

def dfs1(now, p):
	for (x, y, ind, z) in Q[now]:
		ans[ind] += z * (y * count[x] - weight[x])
	for (nex, c, w) in e[now]:
		if nex == p:
			continue
		count[c] += 1
		weight[c] += w
		dfs1(nex, now)
		count[c] -= 1
		weight[c] -= w
dfs1(1, 0)
for i in ans:
	print(i)


import sys
sys.setrecursionlimit(10 ** 6 + 1)

def input():
	x = sys.stdin.readline()
	return x[:-1] if x[-1] == '\n' else x

def printe(*x):
	print('## ', *x, file=sys.stderr)

def printl(li):
	_ = print(*li, sep='\n') if li else None
(N, Q) = map(int, input().split())
edge = [[] for i in range(N)]
for i in range(N - 1):
	(a, b, c, w) = map(int, input().split())
	edge[a - 1].append((b - 1, c - 1, w))
	edge[b - 1].append((a - 1, c - 1, w))

def dfs(start):
	q = [(start, -1, 0, 0)]
	pars = [-1] * N
	depth = [-1] * N
	dist = [-1] * N
	while len(q):
		(e, p, d, dis) = q.pop()
		if depth[e] != -1:
			continue
		pars[e] = p
		depth[e] = d
		dist[e] = dis
		for (ne, c, w) in edge[e]:
			q.append((ne, e, d + 1, dis + w))
	return (pars, depth, dist)
(pars, d, dist) = dfs(0)
ln = N.bit_length()
dp = [[-1] * N for _ in range(ln + 1)]
dp[0] = pars
for i in range(1, ln + 1):
	for j in range(N):
		dp[i][j] = dp[i - 1][dp[i - 1][j]]

def lca(u, v):
	du = d[u]
	dv = d[v]
	if du > dv:
		(du, dv) = (dv, du)
		(u, v) = (v, u)
	dif = dv - du
	for i in range(ln + 1):
		if dif >> i & 1:
			v = dp[i][v]
	if u == v:
		return u
	for i in range(ln, -1, -1):
		pu = dp[i][u]
		pv = dp[i][v]
		if pu != pv:
			(u, v) = (pu, pv)
	return pars[u]
q = Q
qs = tuple((tuple(map(int, input().split())) for i in range(Q)))
ans = [0] * Q
qn = [[] for _ in range(N)]
for (i, (x, y, u, v)) in enumerate(qs):
	a = lca(u - 1, v - 1)
	ans[i] = dist[u - 1] + dist[v - 1] - 2 * dist[a]
	qn[u - 1].append((i, x - 1, y, 1))
	qn[v - 1].append((i, x - 1, y, 1))
	qn[a].append((i, x - 1, y, -2))
cc = [[0, 0] for _ in range(N - 1)]
dfq = [(0, None, 0, -1)]

def dfs2(e, c, d, p):
	if c != None:
		cc[c][0] += 1
		cc[c][1] += d
	for (qi, x, y, sgn) in qn[e]:
		ans[qi] += (y * cc[x][0] - cc[x][1]) * sgn
	for (ne, nc, nd) in edge[e]:
		if ne == pars[e]:
			continue
		dfs2(ne, nc, nd, e)
	if c != None:
		cc[c][0] -= 1
		cc[c][1] -= d
dfs2(0, -1, 0, None)
printl(ans)


import sys
input = sys.stdin.readline
sys.setrecursionlimit(100000)

def read_values():
	return list(map(int, input().split()))

def read_index():
	return [int(x) - 1 for x in input().split()]

def read_list():
	return list(read_values())

def read_lists(N):
	return [read_list() for n in range(N)]

class V:

	def __init__(self, f, v=None):
		self.f = f
		self.v = v

	def __str__(self):
		return str(self.v)

	def ud(self, n):
		if n is None:
			return
		if self.v is None:
			self.v = n
			return
		self.v = self.f(self.v, n)

class LCA:

	def __init__(self, N, L):
		self.L = L
		self.INF = (N, None)
		self.M0 = 2 ** (2 * N - 1).bit_length()
		self.data = [self.INF] * (2 * self.M0)
		self.D = [0] * N
		self.F = [0] * N
		self.S = []
		self.dist = [0] * N
		self.dfs1(0, 0, -1)
		for (i, v) in enumerate(self.S):
			self.data[self.M0 - 1 + i] = (self.D[v], i)
		for i in range(self.M0 - 2, -1, -1):
			self.data[i] = min(self.data[2 * i + 1], self.data[2 * i + 2])

	def dfs1(self, v, d, p, pd=0):
		self.F[v] = len(self.S)
		self.D[v] = d
		self.S.append(v)
		self.dist[v] = pd
		for (w, c, dis) in self.L[v]:
			if w == p:
				continue
			self.dfs1(w, d + 1, v, pd + dis)
			self.S.append(v)

	def _query(self, a, b):
		yield self.INF
		a += self.M0
		b += self.M0
		while a < b:
			if b & 1:
				b -= 1
				yield self.data[b - 1]
			if a & 1:
				yield self.data[a - 1]
				a += 1
			a >>= 1
			b >>= 1

	def query(self, u, v):
		fu = self.F[u]
		fv = self.F[v]
		if fu > fv:
			(fu, fv) = (fv, fu)
		return self.S[min(self._query(fu, fv + 1))[1]]

	def dist(self, v):
		return self.dist[v]

def dfs2(L, v, p, QS, ans, CC, CD):
	for (q, x, y, f) in QS[v]:
		ans[q] += (y * CC[x] - CD[x]) * f
	for (w, c, dis) in L[v]:
		if w == p:
			continue
		CC[c] += 1
		CD[c] += dis
		dfs2(L, w, v, QS, ans, CC, CD)
		CC[c] -= 1
		CD[c] -= dis

def main():
	(N, Q) = read_values()
	L = [[] for _ in range(N)]
	for _ in range(N - 1):
		(a, b, c, d) = read_values()
		a -= 1
		b -= 1
		c -= 1
		L[a].append((b, c, d))
		L[b].append((a, c, d))
	g = LCA(N, L)
	QS = [[] for _ in range(N)]
	ans = [0] * Q
	for q in range(Q):
		(x, y, u, v) = read_values()
		x -= 1
		u -= 1
		v -= 1
		w = g.query(u, v)
		ans[q] = g.dist[u] + g.dist[v] - 2 * g.dist[w]
		QS[u].append((q, x, y, 1))
		QS[v].append((q, x, y, 1))
		QS[w].append((q, x, y, -2))
	CC = [0] * N
	CD = [0] * N
	dfs2(L, 0, -1, QS, ans, CC, CD)
	print('\n'.join(map(str, ans)))

def __starting_point():
	main()
__starting_point()


import sys, bisect, string, math, time, functools, random, fractions
from heapq import heappush, heappop, heapify
from collections import deque, defaultdict, Counter
from itertools import permutations, combinations, groupby
rep = range

def Golf():
	(n, *t) = map(int, open(0).read().split())

def I():
	return int(input())

def S_():
	return input()

def IS():
	return input().split()

def LS():
	return [i for i in input().split()]

def MI():
	return map(int, input().split())

def LI():
	return [int(i) for i in input().split()]

def LI_():
	return [int(i) - 1 for i in input().split()]

def NI(n):
	return [int(input()) for i in range(n)]

def NI_(n):
	return [int(input()) - 1 for i in range(n)]

def StoLI():
	return [ord(i) - 97 for i in input()]

def ItoS(n):
	return chr(n + 97)

def LtoS(ls):
	return ''.join([chr(i + 97) for i in ls])

def RA():
	return map(int, open(0).read().split())

def GI(V, E, ls=None, Directed=False, index=1):
	org_inp = []
	g = [[] for i in range(V)]
	FromStdin = True if ls == None else False
	for i in range(E):
		if FromStdin:
			inp = LI()
			org_inp.append(inp)
		else:
			inp = ls[i]
		if len(inp) == 2:
			(a, b) = inp
			c = 1
		else:
			(a, b, c) = inp
		if index == 1:
			a -= 1
			b -= 1
		aa = (a, c)
		bb = (b, c)
		g[a].append(bb)
		if not Directed:
			g[b].append(aa)
	return (g, org_inp)

def GGI(h, w, search=None, replacement_of_found='.', mp_def={'#': 1, '.': 0}, boundary=1):
	mp = [boundary] * (w + 2)
	found = {}
	for i in range(h):
		s = input()
		for char in search:
			if char in s:
				found[char] = (i + 1) * (w + 2) + s.index(char) + 1
				mp_def[char] = mp_def[replacement_of_found]
		mp += [boundary] + [mp_def[j] for j in s] + [boundary]
	mp += [boundary] * (w + 2)
	return (h + 2, w + 2, mp, found)

def TI(n):
	return GI(n, n - 1)

def accum(ls):
	rt = [0]
	for i in ls:
		rt += [rt[-1] + i]
	return rt

def bit_combination(n, base=2):
	rt = []
	for tb in range(base ** n):
		s = [tb // base ** bt % base for bt in range(n)]
		rt += [s]
	return rt

def gcd(x, y):
	if y == 0:
		return x
	if x % y == 0:
		return y
	while x % y != 0:
		(x, y) = (y, x % y)
	return y

def YN(x):
	print(['NO', 'YES'][x])

def Yn(x):
	print(['No', 'Yes'][x])

def show(*inp, end='\n'):
	if show_flg:
		print(*inp, end=end)
mo = 10 ** 9 + 7
inf = float('inf')
FourNb = [(-1, 0), (1, 0), (0, 1), (0, -1)]
EightNb = [(-1, 0), (1, 0), (0, 1), (0, -1), (1, 1), (-1, -1), (1, -1), (-1, 1)]
compas = dict(zip('WENS', FourNb))
cursol = dict(zip('LRUD', FourNb))
l_alp = string.ascii_lowercase
read = sys.stdin.buffer.read
readline = sys.stdin.buffer.readline
input = lambda : sys.stdin.readline().rstrip()

class Tree:

	def __init__(self, inp_size=None, ls=None, init=True, index=1):
		self.LCA_init_stat = False
		self.ETtable = []
		if init:
			if ls == None:
				self.stdin(inp_size, index=index)
			else:
				self.node_size = len(ls) + 1
				(self.edges, _) = GI(self.node_size, self.node_size - 1, ls, index=index)
		return

	def stdin(self, inp_size=None, index=1):
		if inp_size == None:
			self.node_size = int(input())
		else:
			self.node_size = inp_size
		(self.edges, _) = GI(self.node_size, self.node_size - 1, index=index)
		return

	def listin(self, ls, index=0):
		self.node_size = len(ls) + 1
		(self.edges, _) = GI(self.node_size, self.node_size - 1, ls, index=index)
		return

	def dfs(self, x, func=lambda pr, prv, nx, dist: prv + dist, root_v=0):
		q = deque([x])
		v = [None] * self.node_size
		v[x] = root_v
		while q:
			c = q.pop()
			for (nb, d) in self.edges[c]:
				if v[nb] == None:
					q.append(nb)
					v[nb] = func(c, v[c], nb, d)
		return v

	def bfs(self, x, func=lambda pr, prv, nx, dist: prv + dist, root_v=0):
		q = deque([x])
		v = [None] * self.node_size
		v[x] = root_v
		while q:
			c = q.popleft()
			for (nb, d) in self.edges[c]:
				if v[nb] == None:
					q.append(nb)
					v[nb] = func(c, v[c], nb, d)
		return v

	def parent(self, x):
		return self.dfs(0, func=lambda pr, prv, nx, dist: pr, root_v=-1)

	def topological_sort(self, x):
		tps = []
		q = deque([x])
		v = [None] * self.node_size
		v[x] = 0
		while q:
			c = q.popleft()
			tps.append(c)
			for (nb, d) in self.edges[c]:
				if v[nb] == None:
					q.append(nb)
					v[nb] = 0
		return tps

	def EulerTour(self, x):
		q = deque()
		q.append(x)
		self.depth = [None] * self.node_size
		self.depth[x] = 0
		self.ETtable = []
		self.ETdepth = []
		self.ETin = [-1] * self.node_size
		self.ETout = [-1] * self.node_size
		cnt = 0
		while q:
			c = q.pop()
			if c < 0:
				ce = ~c
			else:
				ce = c
				for (nb, d) in self.edges[ce]:
					if self.depth[nb] == None:
						q.append(~ce)
						q.append(nb)
						self.depth[nb] = self.depth[ce] + 1
			self.ETtable.append(ce)
			self.ETdepth.append(self.depth[ce])
			if self.ETin[ce] == -1:
				self.ETin[ce] = cnt
			else:
				self.ETout[ce] = cnt
			cnt += 1
		return

	def doubling_LCA(self, root, x, y):
		if self.LCA_init_stat == False:
			self.depth = [None] * self.node_size
			self.depth = self.bfs(0, func=lambda pr, prv, nxt, dist: prv + 1)
			self.par = self.bfs(0, func=lambda pr, prv, nxt, dist: pr)
			self.db = [self.par]
			for i in range(self.node_size.bit_length()):
				self.db += [[self.db[-1][self.db[-1][i]] for i in range(self.node_size)]]
			self.LCA_init_stat = True
		dx = self.depth[x]
		dy = self.depth[y]
		if dx > dy:
			(dx, dy) = (dy, dx)
		c = self.node_size.bit_length()
		while c > 0:
			if dx + c <= dy:
				x = self.db[c][x]
				dx += 1 << c
			c >>= 1
		if x == y:
			return x
		c = self.node_size.bit_length()
		while c > 0:
			if self.db[c][x] != self.db[c][y]:
				x = self.db[c][x]
				y = self.db[c][y]
			c >>= 1
		return self.par[x]

	def LCA_init(self, root):
		self.EulerTour(root)
		self.st = SparseTable(self.ETdepth, init_func=min, init_idl=inf)
		self.LCA_init_stat = True
		return

	def LCA(self, root, x, y):
		if self.LCA_init_stat == False:
			self.LCA_init(root)
		(xin, xout) = (self.ETin[x], self.ETout[x])
		(yin, yout) = (self.ETin[y], self.ETout[y])
		a = min(xin, yin)
		b = max(xout, yout, xin, yin)
		id_of_min_dep_in_et = self.st.query_id(a, b + 1)
		return self.ETtable[id_of_min_dep_in_et]

	def __str__(self):
		return str(self.edges)

	def show(self):
		if all([all([d == 1 for (nd, d) in e]) for e in self.edges]):
			print([[nd for (nd, d) in e] for e in self.edges])
		else:
			print(self)

	def dfs2(self, x, func=lambda pr, prv, nx, dist: prv + dist, root_v=0):
		q = deque([x])
		v = [None] * self.node_size
		v[x] = root_v
		dfs_tr = [x]
		while q:
			c = q.pop()
			for (nb, d) in self.edges[c]:
				if v[nb] == None:
					q.append(nb)
					v[nb] = func(c, v[c], nb, d)
					dfs_tr += (nb,)
		return (v, dfs_tr)

class SparseTable:

	def __init__(self, ls, init_func=min, init_idl=float('inf')):
		self.func = init_func
		self.idl = init_idl
		self.size = len(ls)
		self.N0 = self.size.bit_length()
		self.table = [ls[:]]
		self.index = [list(range(self.size))]
		self.lg = [0] * (self.size + 1)
		for i in range(2, self.size + 1):
			self.lg[i] = self.lg[i >> 1] + 1
		for i in range(self.N0):
			tmp = [self.func(self.table[i][j], self.table[i][min(j + (1 << i), self.size - 1)]) for j in range(self.size)]
			tmp_id = [self.index[i][j] if self.table[i][j] == self.func(self.table[i][j], self.table[i][min(j + (1 << i), self.size - 1)]) else self.index[i][min(j + (1 << i), self.size - 1)] for j in range(self.size)]
			self.table += [tmp]
			self.index += [tmp_id]

	def query(self, l, r):
		if r > self.size:
			r = self.size
		N = self.lg[r - l]
		return self.func(self.table[N][l], self.table[N][max(0, r - (1 << N))])

	def query_id(self, l, r):
		if r > self.size:
			r = self.size
		N = self.lg[r - l]
		(a, b) = (self.index[N][l], self.index[N][max(0, r - (1 << N))])
		if self.table[0][a] == self.func(self.table[N][l], self.table[N][max(0, r - (1 << N))]):
			b = a
		return b

	def right_bound(self, pos, x):
		k = (self.size - pos).bit_length()
		for j in range(k)[::-1]:
			nx = pos + (1 << j)
			if nx < n and self.query(pos, nx + 1) <= x:
				pos += 1 << j
		return pos

	def left_bound(self, pos, x):
		k = pos.bit_length()
		for j in range(k)[::-1]:
			nx = pos - (1 << j)
			if 0 <= nx and self.query(nx, pos + 1) <= x:
				pos -= 1 << j
		return pos

	def __str__(self):
		return str(self.table[0])

	def print(self):
		for i in self.table:
			print(*i)
show_flg = False
show_flg = True
ans = 0
(n, q) = LI()
ls = deque()
col = dict()
e_len = dict()
for i in range(n - 1):
	(a, b, c, d) = LI_()
	d += 1
	ls += [(a, b, d)]
	eid = a * n + b if a < b else b * n + a
	col[eid] = c
	e_len[eid] = d
tr = Tree(init=False)
tr.listin(ls, index=0)
root = 0
dist = tr.dfs(root)
tr.EulerTour(0)
dep = tr.depth
et = tr.ETtable
ad = [[0] for i in range(n)]
an = [[0] for i in range(n)]
index = [[] for i in range(n)]
for i in range(2 * n - 1 - 1):
	(a, b) = (et[i], et[i + 1])
	sign = 1 if dep[a] < dep[b] else -1
	eid = a * n + b if a < b else b * n + a
	c = col[eid]
	dd = e_len[eid] * sign
	an[c] += [an[c][-1] + sign]
	ad[c] += [ad[c][-1] + dd]
	index[c] += [i]
for i in range(n):
	ad[i] += [ad[i][-1]]
	an[i] += [an[i][-1]]
for i in range(q):
	(x, y, u, v) = LI_()
	y += 1
	lca = tr.LCA(root, u, v)
	path = dist[u] + dist[v] - dist[lca] * 2
	ui = bisect.bisect_left(index[x], tr.ETin[u])
	vi = bisect.bisect_left(index[x], tr.ETin[v])
	ai = bisect.bisect_left(index[x], tr.ETin[lca])
	c_path = ad[x][ui] + ad[x][vi] - ad[x][ai] * 2
	c_n = an[x][ui] + an[x][vi] - an[x][ai] * 2
	ans = path - c_path + c_n * y
	print(ans)


import sys
sys.setrecursionlimit(10 ** 5 + 5)
from collections import defaultdict, deque
input = sys.stdin.readline

def bfs():
	q = deque([1])
	while q:
		now = q.popleft()
		for (nex, c, w) in e[now]:
			if depth[nex] >= 0:
				continue
			depth[nex] = depth[now] + 1
			dis[nex] = dis[now] + w
			lca[0][nex] = now
			q.append(nex)

def dfs1(now, p, count1, count2):
	for cou in dcount[now]:
		dcount[now][cou] = count1[cou]
	for wei in dweight[now]:
		dweight[now][wei] = count2[wei]
	for (nex, c, w) in e[now]:
		if nex == p:
			continue
		count1[c] += 1
		count2[c] += w
		dfs1(nex, now, count1, count2)
		count1[c] -= 1
		count2[c] -= w
(n, q) = map(int, input().split())
e = [[] for i in range(n + 1)]
for i in range(n - 1):
	(a, b, c, d) = map(int, input().split())
	e[a].append((b, c, d))
	e[b].append((a, c, d))
size = n + 1
bitlen = 19
lca = [[0] * size for i in range(bitlen)]
depth = [-1] * size
dis = [-1] * size
depth[1] = 0
dis[1] = 0
bfs()
for i in range(1, bitlen):
	for j in range(1, size):
		if lca[i - 1][j] > 0:
			lca[i][j] = lca[i - 1][lca[i - 1][j]]

def search(x, y):
	dx = depth[x]
	dy = depth[y]
	if dx < dy:
		(x, y) = (y, x)
		(dx, dy) = (dy, dx)
	dif = dx - dy
	while dif:
		s = dif & -dif
		x = lca[s.bit_length() - 1][x]
		dif -= s
	if x == y:
		return x
	for i in range(bitlen - 1, -1, -1):
		if lca[i][x] != lca[i][y]:
			x = lca[i][x]
			y = lca[i][y]
	return lca[0][x]
Q = [[] for i in range(n + 1)]
ans = []
for i in range(q):
	(x, y, u, v) = map(int, input().split())
	a = search(u, v)
	ans.append(dis[u] + dis[v] - 2 * dis[a])
	Q[u].append((x, y, i, 1))
	Q[v].append((x, y, i, 1))
	Q[a].append((x, y, i, -2))
count = defaultdict(int)
weight = defaultdict(int)

def dfs1(now, p):
	for (x, y, ind, z) in Q[now]:
		ans[ind] += z * (y * count[x] - weight[x])
	for (nex, c, w) in e[now]:
		if nex == p:
			continue
		count[c] += 1
		weight[c] += w
		dfs1(nex, now)
		count[c] -= 1
		weight[c] -= w
dfs1(1, 0)
for i in ans:
	print(i)


def main():
	import sys
	from bisect import bisect_left
	input = sys.stdin.buffer.readline
	int_ = int

	class SparseTable:

		def __init__(self, A):
			self.N = len(A)
			self.K = self.N.bit_length() - 1
			self.table = [[0] * (self.K + 1) for _ in range(self.N)]
			for (i, a) in enumerate(A):
				self.table[i][0] = a
			for k in range(1, self.K + 1):
				for i in range(self.N):
					j = i + (1 << k - 1)
					if j <= self.N - 1:
						self.table[i][k] = STfunc(self.table[i][k - 1], self.table[j][k - 1])
					else:
						self.table[i][k] = self.table[i][k - 1]

		def query(self, l, r):
			k = (r - l).bit_length() - 1
			return STfunc(self.table[l][k], self.table[r - (1 << k)][k])

	def EulerTour(adj, root):
		st = [root]
		ret = []
		seen = [0] * len(adj)
		par = [0] * len(adj)
		depth = [0] * len(adj)
		while st:
			v = st.pop()
			if seen[v]:
				ret.append(v)
				continue
			ret.append(v)
			seen[v] = 1
			if par[v] != 0:
				st.append(par[v])
			for u in adj[v]:
				if seen[u] == 0:
					st.append(u)
					par[u] = v
					depth[u] = depth[v] + 1
		return (ret, depth)
	(N, Q) = map(int_, input().split())
	adj = [[] for _ in range(N + 1)]
	E = {}
	for _ in range(N - 1):
		(a, b, c, d) = map(int_, input().split())
		adj[a].append(b)
		adj[b].append(a)
		E[a * (N + 1) + b] = (c, d)
		E[b * (N + 1) + a] = (c, d)
	(et, depth) = EulerTour(adj, 1)
	left = [-1] * (N + 1)
	right = [-1] * (N + 1)
	dist_from_root = [-1] * (N + 1)
	dist_from_root[1] = 0
	color_info = [[] for _ in range(N)]
	color_d = [[] for _ in range(N)]
	for (i, v) in enumerate(et):
		if left[v] < 0:
			left[v] = i
		right[v] = i
		if i != 0:
			u = et[i - 1]
			(c, d) = E[v * (N + 1) + u]
			if dist_from_root[v] < 0:
				dist_from_root[v] = dist_from_root[u] + d
			if depth[u] < depth[v]:
				color_info[c].append(i)
				if not color_d[c]:
					color_d[c].append((1, d))
				else:
					(prev_num, prev_d) = color_d[c][-1]
					color_d[c].append((prev_num + 1, prev_d + d))
			else:
				color_info[c].append(i)
				(prev_num, prev_d) = color_d[c][-1]
				color_d[c].append((prev_num - 1, prev_d - d))

	def STfunc(a, b):
		if depth[a] < depth[b]:
			return a
		else:
			return b
	ST = SparseTable(et)
	for _ in range(Q):
		(x, y, u, v) = map(int_, input().split())
		lca = ST.query(min(left[u], left[v]), max(right[u], right[v]) + 1)
		if color_info[x]:
			le = len(color_info[x])
			iu = bisect_left(color_info[x], left[u] + 1) - 1
			if iu == -1:
				du = dist_from_root[u]
			else:
				(num_u, dist_u) = color_d[x][iu]
				du = dist_from_root[u] + num_u * y - dist_u
			iv = bisect_left(color_info[x], left[v] + 1) - 1
			if iv == -1:
				dv = dist_from_root[v]
			else:
				(num_v, dist_v) = color_d[x][iv]
				dv = dist_from_root[v] + num_v * y - dist_v
			ilca = bisect_left(color_info[x], left[lca] + 1) - 1
			if ilca == -1:
				dlca = dist_from_root[lca]
			else:
				(num_lca, dist_lca) = color_d[x][ilca]
				dlca = dist_from_root[lca] + num_lca * y - dist_lca
			print(du + dv - 2 * dlca)
		else:
			print(dist_from_root[u] + dist_from_root[v] - 2 * dist_from_root[lca])
main()


class Tree:

	def __init__(self, n, decrement=1):
		self.n = n
		self.edges = [[] for _ in range(n)]
		self.edges2 = [[] for _ in range(n)]
		self.root = None
		self.depth = [-1] * n
		self.size = [1] * n
		self.color = [[] for _ in range(n)]
		self.cost = [0] * n
		self.decrement = decrement

	def add_edge(self, u, v):
		(u, v) = (u - self.decrement, v - self.decrement)
		self.edges[u].append(v)
		self.edges[v].append(u)

	def add_edges(self, edges):
		for (u, v, c, d) in edges:
			(u, v) = (u - self.decrement, v - self.decrement)
			self.edges[u].append(v)
			self.edges[v].append(u)
			self.edges2[u].append((v, c, d))
			self.edges2[v].append((u, c, d))

	def set_root(self, root):
		root -= self.decrement
		self.root = root
		self.par = [-1] * self.n
		self.depth[root] = 0
		self.order = [root]
		next_set = [root]
		while next_set:
			p = next_set.pop()
			for (q, c, d) in self.edges2[p]:
				if self.depth[q] != -1:
					continue
				self.par[q] = p
				self.color[c].append(q)
				self.cost[q] = d
				self.depth[q] = self.depth[p] + 1
				self.order.append(q)
				next_set.append(q)
		for p in self.order[::-1]:
			for q in self.edges[p]:
				if self.par[p] == q:
					continue
				self.size[p] += self.size[q]

	def heavy_light_decomposition(self):
		self.vid = [-1] * self.n
		self.hld = [-1] * self.n
		self.head = [-1] * self.n
		self.head[self.root] = self.root
		self.heavy_node = [-1] * self.n
		next_set = [self.root]
		for i in range(self.n):
			p = next_set.pop()
			self.vid[p] = i
			self.hld[i] = p + self.decrement
			maxs = 0
			for q in self.edges[p]:
				if self.par[p] == q:
					continue
				if maxs < self.size[q]:
					maxs = self.size[q]
					self.heavy_node[p] = q
			for q in self.edges[p]:
				if self.par[p] == q or self.heavy_node[p] == q:
					continue
				self.head[q] = q
				next_set.append(q)
			if self.heavy_node[p] != -1:
				self.head[self.heavy_node[p]] = self.head[p]
				next_set.append(self.heavy_node[p])
		return self.hld

	def lca(self, u, v):
		(u, v) = (u - self.decrement, v - self.decrement)
		while True:
			if self.vid[u] > self.vid[v]:
				(u, v) = (v, u)
			if self.head[u] != self.head[v]:
				v = self.par[self.head[v]]
			else:
				return u + self.decrement

	def distance(self, u, v):
		p = self.lca(u, v)
		(u, v, p) = (u - self.decrement, v - self.decrement, p - self.decrement)
		return self.depth[u] + self.depth[v] - 2 * self.depth[p]

	def find(self, u, v, x):
		return self.distance(u, x) + self.distance(x, v) == self.distance(u, v)

	def path_to_list(self, u, v, edge_query=False):
		(u, v) = (u - self.decrement, v - self.decrement)
		while True:
			if self.vid[u] > self.vid[v]:
				(u, v) = (v, u)
			if self.head[u] != self.head[v]:
				yield (self.vid[self.head[v]], self.vid[v] + 1)
				v = self.par[self.head[v]]
			else:
				yield (self.vid[u] + edge_query, self.vid[v] + 1)
				return

	def point(self, u):
		return self.vid[u - self.decrement]

class SegmentTree:

	def __init__(self, n, op, e):
		self.n = n
		self.op = op
		self.e = e
		self.size = 1 << (self.n - 1).bit_length()
		self.tree = [self.e] * (self.size << 1)

	def built(self, array):
		for i in range(self.n):
			self.tree[self.size + i] = array[i]
		for i in range(self.size - 1, 0, -1):
			self.tree[i] = self.op(self.tree[i << 1], self.tree[i << 1 | 1])

	def update(self, i, x):
		i += self.size
		self.tree[i] = x
		while i > 1:
			i >>= 1
			self.tree[i] = self.op(self.tree[i << 1], self.tree[i << 1 | 1])

	def get(self, l, r):
		l += self.size
		r += self.size
		res_l = self.e
		res_r = self.e
		while l < r:
			if l & 1:
				res_l = self.op(res_l, self.tree[l])
				l += 1
			if r & 1:
				r -= 1
				res_r = self.op(self.tree[r], res_r)
			l >>= 1
			r >>= 1
		return self.op(res_l, res_r)

	def max_right(self, l, f):
		if l == self.n:
			return self.n
		l += self.size
		sm = self.e
		while True:
			while l % 2 == 0:
				l >>= 1
			if not f(self.op(sm, self.tree[l])):
				while l < self.size:
					l = 2 * l
					if f(self.op(sm, self.tree[l])):
						sm = self.op(sm, self.tree[l])
						l += 1
				return l - self.size
			sm = self.op(sm, self.tree[l])
			l += 1
			if l & -l == l:
				break
		return self.n

	def min_left(self, r, f):
		if r == 0:
			return 0
		r += self.size
		sm = self.e
		while True:
			r -= 1
			while r > 1 and r % 2:
				r >>= 1
			if not f(self.op(self.tree[r], sm)):
				while r < self.size:
					r = 2 * r + 1
					if f(self.op(self.tree[r], sm)):
						sm = self.op(self.tree[r], sm)
						r -= 1
				return r + 1 - self.size
			sm = self.op(self.tree[r], sm)
			if r & -r == r:
				break
		return 0

	def __iter__(self):
		for a in self.tree[self.size:self.size + self.n]:
			yield a

	def __str__(self):
		return str(self.tree[self.size:self.size + self.n])
import sys
input = sys.stdin.readline
(N, Q) = map(int, input().split())
tree = Tree(N)
(edges, query) = ([], [[] for _ in range(N)])
for _ in range(N - 1):
	(a, b, c, d) = map(int, input().split())
	edges.append((a, b, c - 1, d))
for i in range(Q):
	(c, y, u, v) = map(int, input().split())
	query[c - 1].append((y, u, v, i))
tree.add_edges(edges)
tree.set_root(1)
hld = tree.heavy_light_decomposition()
e = (0, 0)
op = lambda x, y: (x[0] + y[0], x[1] + y[1])
st = SegmentTree(N, op, e)
res = [0] * Q
for p in range(N):
	st.update(tree.point(p + 1), (tree.cost[p], 0))
for c in range(N):
	for p in tree.color[c]:
		st.update(tree.point(p + 1), (0, 1))
	for (y, u, v, i) in query[c]:
		for (l, r) in tree.path_to_list(u, v, True):
			(cost, cnt) = st.get(l, r)
			res[i] += cost + cnt * y
	for p in tree.color[c]:
		st.update(tree.point(p + 1), (tree.cost[p], 0))
print('\n'.join(map(str, res)))


import sys
from collections import defaultdict
readline = sys.stdin.readline
(N, Q) = map(int, readline().split())

def dfs_w(v, d):
	vs = set([1])
	(S, F, depth, depthh) = ([], [None] + [0] * N, [-1] + [0] * N, [0] + [0] * N)
	iin = [[(0, 0, 0)] for _ in range(N)]
	(stack, path) = ([(1, 0, 0)], [0])
	while stack:
		(v, cc, dpd) = stack.pop()
		if v > 0:
			parent = path[-1]
			path.append(v)
			F[v] = len(S)
			(depth[v], depthh[v]) = (depth[parent] + 1, depthh[parent] + dpd)
			iin[cc].append((iin[cc][-1][0] + 1, iin[cc][-1][1] + dpd, len(S)))
			S.append(v)
			for (u, ucc, udpd) in d[v]:
				if u in vs:
					continue
				vs.add(u)
				stack += [(-v, ucc, udpd), (u, ucc, udpd)]
		else:
			path.pop()
			iin[cc].append((iin[cc][-1][0] - 1, iin[cc][-1][1] - dpd, len(S)))
			S.append(-v)
	return (S, F, depth, depthh, iin)

class SegTree:

	def __init__(self, init_val, n, ide_ele, seg_func):
		self.segfunc = seg_func
		self.num = 2 ** (n - 1).bit_length()
		self.ide_ele = ide_ele
		self.seg = [self.ide_ele] * 2 * self.num
		for i in range(n):
			self.seg[i + self.num - 1] = init_val[i]
		for i in range(self.num - 2, -1, -1):
			self.seg[i] = self.segfunc(self.seg[2 * i + 1], self.seg[2 * i + 2])

	def update(self, k, x):
		k += self.num - 1
		self.seg[k] = x
		while k + 1:
			k = (k - 1) // 2
			self.seg[k] = self.segfunc(self.seg[k * 2 + 1], self.seg[k * 2 + 2])

	def query(self, p, q):
		if q <= p:
			return self.ide_ele
		p += self.num - 1
		q += self.num - 2
		res = self.ide_ele
		while q - p > 1:
			if p & 1 == 0:
				res = self.segfunc(res, self.seg[p])
			if q & 1 == 1:
				res = self.segfunc(res, self.seg[q])
				q -= 1
			p = p // 2
			q = (q - 1) // 2
		if p == q:
			res = self.segfunc(res, self.seg[p])
		else:
			res = self.segfunc(self.segfunc(res, self.seg[p]), self.seg[q])
		return res

def bsearch(target, min_i, max_i, func):
	if func(max_i) <= target:
		return max_i
	if target < func(min_i):
		return None
	index = (max_i + min_i) // 2
	while True:
		if func(index) <= target:
			if target < func(index + 1):
				return index
			(index, min_i) = ((index + 1 + max_i) // 2, index + 1)
			continue
		(index, max_i) = ((index - 1 + min_i) // 2, index - 1)

def process():
	G = [set() for _ in range(N + 1)]
	for _ in range(N - 1):
		(a, b, c, d) = map(int, readline().split())
		G[a].add((b, c, d))
		G[b].add((a, c, d))
	(S, F, depth, depthh, iin) = dfs_w(1, G)
	stree = SegTree([(depth[v], i) for (i, v) in enumerate(S)], len(S), (N, None), min)
	for _ in range(Q):
		(x, y, u, v) = map(int, readline().split())
		(fu, fv) = sorted([F[u], F[v]])
		cc = S[stree.query(fu, fv + 1)[1]]
		((fuy, fuv, _), (fvy, fvv, _), (fccy, fccv, _)) = [iin[x][bsearch(index, 0, len(iin[x]) - 1, lambda j: iin[x][j][2])] for index in [fu, fv, F[cc]]]
		r = depthh[u] + depthh[v] - 2 * depthh[cc] + (fuy + fvy - 2 * fccy) * y - (fuv + fvv - 2 * fccv)
		print(r)
process()


import sys
input = sys.stdin.readline
from collections import deque

class LCA:

	def __init__(self, v, Edges, root=0):
		self.v = v
		self.Edges = Edges
		self.maxLog = 18
		self.Parent = [[-1] * v for _ in range(self.maxLog + 1)]
		self.Depth = [0] * v
		self.__bfs(root)
		for i in range(self.maxLog):
			for j in range(v):
				if self.Parent[i][j] != -1:
					self.Parent[i + 1][j] = self.Parent[i][self.Parent[i][j]]

	def __bfs(self, root):
		Visited = [False] * self.v
		Visited[root] = True
		q = deque([root])
		while q:
			fr = q.pop()
			for to in self.Edges[fr]:
				if Visited[to]:
					continue
				self.Parent[0][to] = fr
				self.Depth[to] = self.Depth[fr] + 1
				Visited[to] = True
				q.append(to)

	def lca(self, a, b):
		if self.Depth[a] > self.Depth[b]:
			(a, b) = (b, a)
		for i in range(self.maxLog):
			if self.Depth[b] - self.Depth[a] & 1 << i:
				b = self.Parent[i][b]
		if a == b:
			return b
		for i in reversed(range(self.maxLog - 1)):
			if self.Parent[i][a] != self.Parent[i][b]:
				a = self.Parent[i][a]
				b = self.Parent[i][b]
		return self.Parent[0][a]

def Eulertour(Edges, n, root=0):
	Euler = []
	Depth = [-1] * n
	Depth[root] = 0
	que = deque([root])
	que2 = deque()
	Visited = [False] * n
	while que:
		fr = que.pop()
		Euler.append((Depth[fr], fr))
		if Visited[fr]:
			continue
		for to in Edges[fr]:
			if Visited[to]:
				que.append(to)
			else:
				Depth[to] = Depth[fr] + 1
				que2.append(to)
		que.extend(que2)
		que2 = deque()
		Visited[fr] = True
	return Euler
(n, q) = map(int, input().split())
Edgedict = [dict() for _ in range(n)]
for _ in range(n - 1):
	(a, b, c, d) = map(int, input().split())
	a -= 1
	b -= 1
	Edgedict[a][b] = (c, d)
	Edgedict[b][a] = (c, d)
Queries = [tuple(map(int, input().split())) for _ in range(q)]
Euler = Eulertour(Edgedict, n)
lca = LCA(n, Edgedict)
Q = [[] for _ in range(n)]
for (i, (x, y, u, v)) in enumerate(Queries):
	u -= 1
	v -= 1
	Q[u].append([i, x, y, True])
	Q[v].append([i, x, y, True])
	Q[lca.lca(u, v)].append([i, x, y, False])
Ans = [0] * q
dist = 0
C_len = [0] * n
C_cnt = [0] * n
for i in range(1, len(Euler)):
	idx = Euler[i][1]
	(c, l) = Edgedict[Euler[i - 1][1]][idx]
	if Euler[i - 1][0] < Euler[i][0]:
		dist += l
		C_len[c] += l
		C_cnt[c] += 1
		for (q_idx, x, y, flag) in Q[idx]:
			if flag:
				Ans[q_idx] += dist + C_cnt[x] * y - C_len[x]
			else:
				Ans[q_idx] -= (dist + C_cnt[x] * y - C_len[x]) * 2
	else:
		dist -= l
		C_len[c] -= l
		C_cnt[c] -= 1
print(*Ans, sep='\n')


import sys
input = sys.stdin.readline
sys.setrecursionlimit(10 ** 5 + 100)
from collections import *

def bfs():
	q = deque([0])
	dep = [-1] * N
	dist = [-1] * N
	dep[0] = 0
	dist[0] = 0
	while q:
		v = q.popleft()
		for (nv, _, w) in G[v]:
			if dep[nv] == -1:
				parent[0][nv] = v
				dep[nv] = dep[v] + 1
				dist[nv] = dist[v] + w
				q.append(nv)
	return (dep, dist)

def lca(u, v):
	if dep[u] > dep[v]:
		(u, v) = (v, u)
	for i in range(log_size):
		if dep[v] - dep[u] >> i & 1:
			v = parent[i][v]
	if u == v:
		return u
	for i in range(log_size - 1, -1, -1):
		if parent[i][u] != parent[i][v]:
			(u, v) = (parent[i][u], parent[i][v])
	return parent[0][u]

def calc_dist(u, v):
	return dist[u] + dist[v] - 2 * dist[lca(u, v)]

def dfs(v, pv, now_cnt, now_wet):
	for c in cnt[v]:
		cnt[v][c] = now_cnt[c]
	for c in wet[v]:
		wet[v][c] = now_wet[c]
	for (nv, c, w) in G[v]:
		if nv == pv:
			continue
		now_cnt[c] += 1
		now_wet[c] += w
		dfs(nv, v, now_cnt, now_wet)
		now_cnt[c] -= 1
		now_wet[c] -= w
(N, Q) = map(int, input().split())
G = [[] for _ in range(N)]
for _ in range(N - 1):
	(a, b, c, d) = map(int, input().split())
	G[a - 1].append((b - 1, c, d))
	G[b - 1].append((a - 1, c, d))
log_size = 20
parent = [[-1] * N for _ in range(log_size)]
(dep, dist) = bfs()
for i in range(1, log_size):
	for v in range(N):
		if parent[i - 1][v] >= 0:
			parent[i][v] = parent[i - 1][parent[i - 1][v]]
xyuv = [tuple(map(int, input().split())) for _ in range(Q)]
cnt = defaultdict(lambda : defaultdict(int))
wet = defaultdict(lambda : defaultdict(int))
for (x, y, u, v) in xyuv:
	cnt[u - 1][x] = -1
	cnt[v - 1][x] = -1
	cnt[lca(u - 1, v - 1)][x] = -1
	wet[u - 1][x] = -1
	wet[v - 1][x] = -1
	wet[lca(u - 1, v - 1)][x] = -1
dfs(0, -1, defaultdict(int), defaultdict(int))
for (x, y, u, v) in xyuv:
	l = lca(u - 1, v - 1)
	decr = wet[u - 1][x] + wet[v - 1][x] - 2 * wet[l][x]
	incr = y * (cnt[u - 1][x] + cnt[v - 1][x] - 2 * cnt[l][x])
	print(calc_dist(u - 1, v - 1) - decr + incr)


import sys
input = sys.stdin.readline
sys.setrecursionlimit(10 ** 7)

class LCA:

	def __init__(self, edge, root):
		self.l = len(edge)
		self.stack = []
		self.stack.append((root, 0))
		self.vs = []
		self.depth = [0] * self.l
		self.firstVisited = [-1] * self.l
		while self.stack:
			(v, d) = self.stack[-1]
			if self.firstVisited[v] == -1:
				self.firstVisited[v] = len(self.vs)
			self.vs.append(v)
			self.depth[v] = d
			if edge[v]:
				self.stack.append((edge[v].pop(), d + 1))
			else:
				self.stack.pop()
		self.n = len(self.vs)
		self.INF = (self.l, self.l)
		self.N0 = 1 << (self.n - 1).bit_length()
		self.seg = [self.INF] * (self.N0 << 1)
		for (i, v) in enumerate(self.vs, self.N0):
			self.seg[i] = (self.depth[v], v)
		for i in range(self.N0 - 1, 0, -1):
			self.seg[i] = min(self.seg[2 * i], self.seg[2 * i + 1])

	def _query(self, l, r):
		res = self.INF
		l += self.N0
		r += self.N0
		while l < r:
			if r & 1:
				res = min(res, self.seg[r - 1])
			if l & 1:
				res = min(res, self.seg[l])
				l += 1
			l >>= 1
			r >>= 1
		return res

	def query(self, u, v):
		(fu, fv) = (self.firstVisited[u], self.firstVisited[v])
		if fu > fv:
			(fu, fv) = (fv, fu)
		return self._query(fu, fv + 1)[1]
(n, q) = map(int, input().split())
T = [[] for _ in range(n)]
for _ in range(n - 1):
	(a, b, c, d) = map(int, input().split())
	a -= 1
	b -= 1
	T[a].append((b, c, d))
	T[b].append((a, c, d))
G = [[] for _ in range(n)]
edge = [[] for _ in range(n)]
stack = [(0, -1)]
while stack:
	(v, par) = stack.pop()
	for (nv, c, d) in T[v]:
		if nv == par:
			continue
		G[v].append((nv, c, d))
		edge[v].append(nv)
		stack.append((nv, v))
seg = LCA(edge, 0)
A = [0] * q
C = [[] for _ in range(n)]
num = [0] * n
cum = [0] * n
for i in range(q):
	(x, y, u, v) = map(int, input().split())
	u -= 1
	v -= 1
	a = seg.query(u, v)
	C[u].append((i, x, y, 1))
	C[v].append((i, x, y, 1))
	C[a].append((i, x, y, -2))
dist = 0
stack = [0]

def dfs(v):
	global dist
	for (i, x, y, b) in C[v]:
		A[i] += (dist + num[x] * y - cum[x]) * b
	for (nv, c, d) in G[v]:
		dist += d
		num[c] += 1
		cum[c] += d
		dfs(nv)
		dist -= d
		num[c] -= 1
		cum[c] -= d
dfs(0)
print(*A, sep='\n')


import sys
input = sys.stdin.readline
(N, Q) = map(int, input().split())
EDGE = [list(map(int, input().split())) for i in range(N - 1)]
Query = [list(map(int, input().split())) for i in range(Q)]
mod = 10 ** 9 + 7
EDGELIST = [dict() for i in range(N + 1)]
for (x, y, c, l) in EDGE:
	EDGELIST[x][y] = (c, l)
	EDGELIST[y][x] = (c, l)
DEPTH = [-1] * (N + 1)
DEPTH[1] = 0
from collections import deque
QUE = deque([1])
QUE2 = deque()
EULER = []
USED = [0] * (N + 1)
while QUE:
	x = QUE.pop()
	EULER.append((DEPTH[x], x))
	if USED[x] == 1:
		continue
	for to in EDGELIST[x]:
		if USED[to] == 0:
			DEPTH[to] = DEPTH[x] + 1
			QUE2.append(to)
		else:
			QUE.append(to)
	QUE.extend(QUE2)
	QUE2 = deque()
	USED[x] = 1
MINP = [1 << 30] * (N + 1)
MAXP = [-1] * (N + 1)
for (ind, (depth, p)) in enumerate(EULER):
	MINP[p] = min(MINP[p], ind)
	MAXP[p] = max(MAXP[p], ind)
LEN = len(EULER)
seg_el = 1 << LEN.bit_length()
SEG = [(1 << 30, 0)] * (2 * seg_el)
for i in range(LEN):
	SEG[i + seg_el] = EULER[i]
for i in range(seg_el - 1, 0, -1):
	SEG[i] = min(SEG[i * 2], SEG[i * 2 + 1])

def update(n, x, seg_el):
	i = n + seg_el
	SEG[i] = x
	i >>= 1
	while i != 0:
		SEG[i] = min(SEG[i * 2], SEG[i * 2 + 1])
		i >>= 1

def getvalues(l, r):
	L = l + seg_el
	R = r + seg_el
	ANS = (1 << 30, 0)
	while L < R:
		if L & 1:
			ANS = min(ANS, SEG[L])
			L += 1
		if R & 1:
			R -= 1
			ANS = min(ANS, SEG[R])
		L >>= 1
		R >>= 1
	return ANS

def LCA(l, r):
	return getvalues(min(MINP[l], MINP[r]), max(MAXP[l], MAXP[r]) + 1)
check = [set() for i in range(N + 1)]
for (c, l, x, y) in Query:
	check[x].add(c)
	check[y].add(c)
	check[LCA(x, y)[1]].add(c)
LENGTH = [0] * (N + 1)
LENG = 0
C_LENGTH = [0] * N
C_SUM = [0] * N
C_LDICT = dict()
C_SDICT = dict()
for c in check[1]:
	C_LDICT[1, c] = 0
	C_SDICT[1, c] = 0
for i in range(1, len(EULER)):
	ind = EULER[i][1]
	(c, l) = EDGELIST[EULER[i - 1][1]][ind]
	if EULER[i][0] > EULER[i - 1][0]:
		LENG += l
		C_LENGTH[c] += l
		C_SUM[c] += 1
		LENGTH[ind] = LENG
		for c in check[ind]:
			C_LDICT[ind, c] = C_LENGTH[c]
			C_SDICT[ind, c] = C_SUM[c]
	else:
		LENG -= l
		C_LENGTH[c] -= l
		C_SUM[c] -= 1
for (c, pl, x, y) in Query:
	ind = LCA(x, y)[1]
	print(LENGTH[x] + LENGTH[y] - LENGTH[ind] * 2 + (C_SDICT[x, c] + C_SDICT[y, c] - C_SDICT[ind, c] * 2) * pl - (C_LDICT[x, c] + C_LDICT[y, c] - C_LDICT[ind, c] * 2))


import sys
input = lambda : sys.stdin.readline().rstrip()
sys.setrecursionlimit(10 ** 8)

class LCA(object):

	def __init__(self, to, root=0):
		self.__n = len(to)
		self.__to = to
		self.__logn = (self.__n - 1).bit_length()
		self.__depth = [-1] * self.__n
		self.__dist = [-1] * self.__n
		self.__depth[root] = 0
		self.__dist[root] = 0
		self.__parents = [[-1] * self.__n for _ in range(self.__logn)]
		self.__dfs(root)
		self.__doubling()

	def __dfs(self, v):
		for (u, c, d) in self.__to[v]:
			if self.__depth[u] != -1:
				continue
			self.__parents[0][u] = v
			self.__depth[u] = self.__depth[v] + 1
			self.__dist[u] = self.__dist[v] + d
			self.__dfs(u)

	def __doubling(self):
		for i in range(1, self.__logn):
			for v in range(self.__n):
				if self.__parents[i - 1][v] == -1:
					continue
				self.__parents[i][v] = self.__parents[i - 1][self.__parents[i - 1][v]]

	@property
	def depth(self):
		return self.__depth

	@property
	def dist(self):
		return self.__dist

	def get(self, u, v):
		dd = self.__depth[v] - self.__depth[u]
		if dd < 0:
			(u, v) = (v, u)
			dd *= -1
		for i in range(self.__logn):
			if dd & 2 ** i:
				v = self.__parents[i][v]
		if v == u:
			return v
		for i in range(self.__logn - 1, -1, -1):
			pu = self.__parents[i][u]
			pv = self.__parents[i][v]
			if pu != pv:
				(u, v) = (pu, pv)
		return self.__parents[0][u]

def resolve():
	(n, q) = map(int, input().split())
	to = [[] for _ in range(n)]
	for _ in range(n - 1):
		(a, b, c, d) = map(int, input().split())
		a -= 1
		b -= 1
		to[a].append((b, c, d))
		to[b].append((a, c, d))
	G = LCA(to)
	Query = [[] for _ in range(n)]
	for i in range(q):
		(x, y, u, v) = map(int, input().split())
		u -= 1
		v -= 1
		c = G.get(u, v)
		Query[u].append((i, x, y, 1))
		Query[v].append((i, x, y, 1))
		Query[c].append((i, x, y, -2))
	ans = [0] * q
	S = [[0, 0] for _ in range(n)]

	def dfs(v, p=-1):
		for (idx, color, mag, coef) in Query[v]:
			x = G.dist[v]
			x -= S[color][1]
			x += mag * S[color][0]
			ans[idx] += x * coef
		for (nv, color, d) in to[v]:
			if nv == p:
				continue
			S[color][0] += 1
			S[color][1] += d
			dfs(nv, v)
			S[color][0] -= 1
			S[color][1] -= d
	dfs(0)
	print(*ans, sep='\n')
resolve()


import sys
sys.setrecursionlimit(10 ** 7)
(N, Q) = map(int, input().split())
edge = [[] for _ in range(N + 1)]
for _ in range(N - 1):
	(a, b, c, d) = map(int, input().split())
	edge[a].append((b, c, d))
	edge[b].append((a, c, d))
depth = [0] * (N + 1)
U = 17
ancestor = [[0] * (U + 1) for _ in range(N + 1)]
q = [(1, 0, 0)]
while q:
	qq = []
	for (x, d, parent) in q:
		depth[x] = d
		ax = ancestor[x]
		ax[0] = parent
		for i in range(d.bit_length() - 1):
			ax[i + 1] = ancestor[ax[i]][i]
		for (y, _, _) in edge[x]:
			if y == parent:
				continue
			qq.append((y, d + 1, x))
	q = qq

def lca(x, y):
	dx = depth[x]
	dy = depth[y]
	if dx > dy:
		(x, y) = (y, x)
		(dx, dy) = (dy, dx)
	diff = dy - dx
	while diff:
		step = diff & -diff
		y = ancestor[y][step.bit_length() - 1]
		diff -= step
	while x != y:
		j = 0
		while ancestor[x][j] != ancestor[y][j]:
			j += 1
		if j == 0:
			return ancestor[x][0]
		x = ancestor[x][j - 1]
		y = ancestor[y][j - 1]
	return x
task = [[] for _ in range(N + 1)]
for i in range(Q):
	(x, y, u, v) = map(int, input().split())
	task[u].append((i, x, y, 1))
	task[v].append((i, x, y, 1))
	task[lca(u, v)].append((i, x, y, -2))
answer = [0] * Q

def dfs(x=1, sums=[0] * (N + 1), nums=[0] * (N + 1), total=0, parent=0):
	for (i, c, d, coef) in task[x]:
		answer[i] += coef * (total - sums[c] + nums[c] * d)
	for (y, c, d) in edge[x]:
		if y == parent:
			continue
		sums[c] += d
		nums[c] += 1
		total += d
		dfs(y, sums, nums, total, x)
		sums[c] -= d
		nums[c] -= 1
		total -= d
	return
dfs()
print('\n'.join(map(str, answer)))


import sys, bisect
from collections import deque

def I():
	return int(input())

def LI():
	return [int(i) for i in input().split()]

def LI_():
	return [int(i) - 1 for i in input().split()]

def GI(V, E, ls=None, Directed=False, index=1):
	org_inp = []
	g = [[] for i in range(V)]
	FromStdin = True if ls == None else False
	for i in range(E):
		if FromStdin:
			inp = LI()
			org_inp.append(inp)
		else:
			inp = ls[i]
		if len(inp) == 2:
			(a, b) = inp
			c = 1
		else:
			(a, b, c) = inp
		if index == 1:
			a -= 1
			b -= 1
		aa = (a, c)
		bb = (b, c)
		g[a].append(bb)
		if not Directed:
			g[b].append(aa)
	return (g, org_inp)

def TI(n):
	return GI(n, n - 1)
mo = 10 ** 9 + 7
inf = float('inf')
input = lambda : sys.stdin.readline().rstrip()

class Tree:

	def __init__(self, inp_size=None, init=True):
		self.LCA_init_stat = False
		self.ETtable = []
		if init:
			self.stdin(inp_size)
		return

	def stdin(self, inp_size=None, index=1):
		if inp_size == None:
			self.size = int(input())
		else:
			self.size = inp_size
		(self.edges, _) = GI(self.size, self.size - 1, index=index)
		return

	def listin(self, ls, index=0):
		self.size = len(ls) + 1
		(self.edges, _) = GI(self.size, self.size - 1, ls, index=index)
		return

	def __str__(self):
		return str(self.edges)

	def dfs(self, x, func=lambda prv, nx, dist: prv + dist, root_v=0):
		q = deque()
		q.append(x)
		v = [-1] * self.size
		v[x] = root_v
		while q:
			c = q.pop()
			for (nb, d) in self.edges[c]:
				if v[nb] == -1:
					q.append(nb)
					v[nb] = func(v[c], nb, d)
		return v

	def EulerTour(self, x):
		q = deque()
		q.append(x)
		self.depth = [None] * self.size
		self.depth[x] = 0
		self.ETtable = []
		self.ETdepth = []
		self.ETin = [-1] * self.size
		self.ETout = [-1] * self.size
		cnt = 0
		while q:
			c = q.pop()
			if c < 0:
				ce = ~c
			else:
				ce = c
				for (nb, d) in self.edges[ce]:
					if self.depth[nb] == None:
						q.append(~ce)
						q.append(nb)
						self.depth[nb] = self.depth[ce] + 1
			self.ETtable.append(ce)
			self.ETdepth.append(self.depth[ce])
			if self.ETin[ce] == -1:
				self.ETin[ce] = cnt
			else:
				self.ETout[ce] = cnt
			cnt += 1
		return

	def LCA_init(self, root):
		self.EulerTour(root)
		self.st = SparseTable(self.ETdepth, init_func=min, init_idl=inf)
		self.LCA_init_stat = True
		return

	def LCA(self, root, x, y):
		if self.LCA_init_stat == False:
			self.LCA_init(root)
		(xin, xout) = (self.ETin[x], self.ETout[x])
		(yin, yout) = (self.ETin[y], self.ETout[y])
		a = min(xin, yin)
		b = max(xout, yout, xin, yin)
		id_of_min_dep_in_et = self.st.query_id(a, b + 1)
		return self.ETtable[id_of_min_dep_in_et]

class SparseTable:

	def __init__(self, ls, init_func=min, init_idl=float('inf')):
		self.func = init_func
		self.idl = init_idl
		self.size = len(ls)
		self.N0 = self.size.bit_length()
		self.table = [ls[:]]
		self.index = [list(range(self.size))]
		self.lg = [0] * (self.size + 1)
		for i in range(2, self.size + 1):
			self.lg[i] = self.lg[i >> 1] + 1
		for i in range(self.N0):
			tmp = [self.func(self.table[i][j], self.table[i][min(j + (1 << i), self.size - 1)]) for j in range(self.size)]
			tmp_id = [self.index[i][j] if self.table[i][j] == self.func(self.table[i][j], self.table[i][min(j + (1 << i), self.size - 1)]) else self.index[i][min(j + (1 << i), self.size - 1)] for j in range(self.size)]
			self.table += [tmp]
			self.index += [tmp_id]

	def query(self, l, r):
		N = self.lg[r - l]
		return self.func(self.table[N][l], self.table[N][r - (1 << N)])

	def query_id(self, l, r):
		N = self.lg[r - l]
		(a, b) = (self.index[N][l], self.index[N][r - (1 << N)])
		if self.table[0][a] == self.func(self.table[N][l], self.table[N][r - (1 << N)]):
			b = a
		return b
ans = 0
(n, q) = LI()
ls = deque()
col = dict()
e_len = dict()
for i in range(n - 1):
	(a, b, c, d) = LI_()
	d += 1
	ls += [(a, b, d)]
	eid = a * n + b if a < b else b * n + a
	col[eid] = c
	e_len[eid] = d
tr = Tree(init=False)
tr.listin(ls, index=0)
root = 0
dist = tr.dfs(root)
tr.EulerTour(0)
dep = tr.depth
et = tr.ETtable
ad = [[0] for i in range(n)]
an = [[0] for i in range(n)]
index = [[] for i in range(n)]
for i in range(2 * n - 1 - 1):
	(a, b) = (et[i], et[i + 1])
	sign = 1 if dep[a] < dep[b] else -1
	eid = a * n + b if a < b else b * n + a
	c = col[eid]
	dd = e_len[eid] * sign
	an[c] += [an[c][-1] + sign]
	ad[c] += [ad[c][-1] + dd]
	index[c] += [i]
for i in range(n):
	ad[i] += [ad[i][-1]]
	an[i] += [an[i][-1]]
for i in range(q):
	(x, y, u, v) = LI_()
	y += 1
	lca = tr.LCA(root, u, v)
	path = dist[u] + dist[v] - dist[lca] * 2
	ui = bisect.bisect_left(index[x], tr.ETin[u])
	vi = bisect.bisect_left(index[x], tr.ETin[v])
	ai = bisect.bisect_left(index[x], tr.ETin[lca])
	c_path = ad[x][ui] + ad[x][vi] - ad[x][ai] * 2
	c_n = an[x][ui] + an[x][vi] - an[x][ai] * 2
	ans = path - c_path + c_n * y
	print(ans)


import sys
input = sys.stdin.readline

def I():
	return int(input())

def MI():
	return map(int, input().split())

def LI():
	return list(map(int, input().split()))

def main():
	mod = 10 ** 9 + 7
	(N, Q) = MI()
	adj = [[] for _ in range(N)]
	from collections import defaultdict
	uv2cd = defaultdict(set)
	for _ in range(N - 1):
		(a, b, c, d) = MI()
		a -= 1
		b -= 1
		c -= 1
		adj[a].append(b)
		adj[b].append(a)
		uv2cd[a, b] = (c, d)
		uv2cd[b, a] = (c, d)

	def EulerTour(n, X, i0):
		P = [-1] * n
		st = [~i0, i0]
		ct = -1
		ET = []
		ET1 = [0] * n
		ET2 = [0] * n
		DE = [0] * n
		de = -1
		while st:
			i = st.pop()
			if i < 0:
				ct += 1
				ET.append(P[~i])
				ET2[~i] = ct
				de -= 1
				continue
			if i >= 0:
				ET.append(i)
				ct += 1
				if ET1[i] == 0:
					ET1[i] = ct
				de += 1
				DE[i] = de
			for a in X[i][::-1]:
				if a != P[i]:
					P[a] = i
					for k in range(len(X[a])):
						if X[a][k] == i:
							del X[a][k]
							break
					st.append(~a)
					st.append(a)
		return (ET, ET1, ET2, DE, P)
	(S, F, Frev, depth, P) = EulerTour(N, adj, 0)
	INF = (N, None)
	M = 2 * N
	M0 = 2 ** (M - 1).bit_length()
	data = [INF] * (2 * M0)
	for (i, v) in enumerate(S):
		data[M0 - 1 + i] = (depth[v], i)
	for i in range(M0 - 2, -1, -1):
		data[i] = min(data[2 * i + 1], data[2 * i + 2])

	def _query(a, b):
		yield INF
		a += M0
		b += M0
		while a < b:
			if b & 1:
				b -= 1
				yield data[b - 1]
			if a & 1:
				yield data[a - 1]
				a += 1
			a >>= 1
			b >>= 1

	def query(u, v):
		fu = F[u]
		fv = F[v]
		if fu > fv:
			(fu, fv) = (fv, fu)
		return S[min(_query(fu, fv + 1))[1]]
	QL = []
	v2x = defaultdict(set)
	for _ in range(Q):
		(x, y, u, v) = MI()
		x -= 1
		u -= 1
		v -= 1
		lca = query(u, v)
		QL.append((x, y, u, v, lca))
		v2x[u].add(x)
		v2x[v].add(x)
		v2x[lca].add(x)
	count_xv = defaultdict(int)
	dist_xv = defaultdict(int)
	count = [0] * N
	dist = [0] * N
	used = [0] * N
	used[0] = 1
	dist_nonchange = [0] * N
	dsum = 0
	now = 0
	for nxt in S[1:-1]:
		(x, d) = uv2cd[now, nxt]
		if used[nxt] == 0:
			count[x] += 1
			dist[x] += d
			used[nxt] = 1
			dsum += d
			dist_nonchange[nxt] = dsum
			for xx in v2x[nxt]:
				count_xv[xx, nxt] = count[xx]
				dist_xv[xx, nxt] = dist[xx]
		else:
			count[x] -= 1
			dist[x] -= d
			dsum -= d
		now = nxt
	for i in range(Q):
		(x, y, u, v, lca) = QL[i]
		tempu = dist_nonchange[u] - dist_xv[x, u] + y * count_xv[x, u]
		tempv = dist_nonchange[v] - dist_xv[x, v] + y * count_xv[x, v]
		templca = dist_nonchange[lca] - dist_xv[x, lca] + y * count_xv[x, lca]
		ans = tempu + tempv - 2 * templca
		print(ans)
main()


import bisect
import math
import os
import sys
from collections import defaultdict
if os.getenv('LOCAL'):
	sys.stdin = open('_in.txt', 'r')
sys.setrecursionlimit(2147483647)
INF = float('inf')
IINF = 10 ** 18
MOD = 10 ** 9 + 7

class SparseTable:

	def __init__(self, values, fn):
		self._values = values
		self._fn = fn
		self._table = self._build(values, fn)
		self._msb = [0] * (len(values) + 1)
		for i in range(2, len(values) + 1):
			self._msb[i] = self._msb[i >> 1] + 1

	@staticmethod
	def _build(values, fn):
		size = int(math.log(len(values), 2)) + 1
		st = [[0] * size for _ in range(len(values))]
		for i in range(len(values)):
			st[i][0] = i
		for p in range(1, size):
			for i in range(len(values)):
				q = min(i + (1 << p - 1), len(values) - 1)
				l = st[i][p - 1]
				r = st[q][p - 1]
				if values[l] == fn(values[l], values[r]):
					st[i][p] = l
				else:
					st[i][p] = r
		return st

	def get(self, a, b):
		if b <= a:
			return None
		p = self._msb[b - a]
		return self._fn(self._values[self._table[a][p]], self._values[self._table[b - (1 << p)][p]])

def eulerian_trail(tree, max_v, root=0):
	seen = [False] * (max_v + 1)
	trails = []
	depths = []
	edges = []
	stack = [(root, 0, (0, 0), True)]
	while stack:
		(v, d, edge, forward) = stack.pop()
		seen[v] = True
		trails.append(v)
		depths.append(d)
		edges.append(edge)
		if not forward:
			continue
		for (u, c, w) in tree[v]:
			if not seen[u]:
				stack.append((v, d, (c, -w), False))
				stack.append((u, d + 1, (c, w), True))
	return (trails, depths, edges)
(N, Q) = list(map(int, sys.stdin.readline().split()))
ABCD = [list(map(int, sys.stdin.readline().split())) for _ in range(N - 1)]
XYVU = [list(map(int, sys.stdin.readline().split())) for _ in range(Q)]
graph = [[] for _ in range(N + 1)]
for (a, b, c, d) in ABCD:
	graph[a].append((b, c, d))
	graph[b].append((a, c, d))
(trails, depths, edges) = eulerian_trail(graph, max_v=N, root=1)
ids = [-1] * (N + 1)
for (i, t) in enumerate(trails):
	ids[t] = i
distances = []
color_dist = defaultdict(lambda : [(-1, 0)])
color_cnt = defaultdict(lambda : [(-1, 0)])
s = 0
for (i, (c, d)) in enumerate(edges):
	s += d
	distances.append(s)
	color_dist[c].append((i, color_dist[c][-1][1] + d))
	(_, cc) = color_cnt[c][-1]
	color_cnt[c].append((i, cc + 1 if d > 0 else cc - 1))
st = SparseTable(values=list(zip(depths, trails)), fn=min)

def get_lca(v, u):
	(iv, iu) = (ids[v], ids[u])
	if iv > iu:
		(iv, iu) = (iu, iv)
	(_, ret) = st.get(iv, iu + 1)
	return ret

def distance(v):
	return distances[ids[v]]

def count_color(v, c):
	iv = ids[v]
	i = bisect.bisect_left(color_cnt[c], (iv + 1, 0))
	return color_cnt[c][i - 1][1]

def distance_color(v, c):
	iv = ids[v]
	i = bisect.bisect_left(color_dist[c], (iv + 1, 0))
	return color_dist[c][i - 1][1]
for (x, y, u, v) in XYVU:
	lca = get_lca(u, v)
	d = distance(u) + distance(v) - distance(lca) * 2
	c = count_color(u, x) + count_color(v, x) - count_color(lca, x) * 2
	s = distance_color(u, x) + distance_color(v, x) - distance_color(lca, x) * 2
	print(d - s + c * y)


import sys
from collections import defaultdict
sys.setrecursionlimit(10 ** 5 + 5)

def solve(n, links, queries):

	def _query(a, b, data, m0, INF):
		yield INF
		a += m0
		b += m0
		while a < b:
			if b & 1:
				b -= 1
				yield data[b - 1]
			if a & 1:
				yield data[a - 1]
				a += 1
			a >>= 1
			b >>= 1

	def query(u, v):
		fu = first_appear[u]
		fv = first_appear[v]
		if fu > fv:
			(fu, fv) = (fv, fu)
		return euler_tour[min(_query(fu, fv + 1, data, m0, INF))[1]]

	def dfs(v, p, dep):
		first_appear[v] = len(euler_tour)
		depth[v] = dep
		euler_tour.append(v)
		for (u, _, _) in links[v]:
			if u == p:
				continue
			dfs(u, v, dep + 1)
			euler_tour.append(v)

	def dfs2(v, p, tmp_dists, total_dist):
		for (x, arr) in v_queries[v].items():
			arr[0] = total_dist - tmp_dists[x][0]
			arr[1] = tmp_dists[x][1]
		for (u, uc, ud) in links[v]:
			if u == p:
				continue
			tmp_dists[uc][0] += ud
			tmp_dists[uc][1] += 1
			dfs2(u, v, tmp_dists, total_dist + ud)
			tmp_dists[uc][0] -= ud
			tmp_dists[uc][1] -= 1
	INF = (n, None)
	euler_tour = []
	first_appear = [0] * n
	depth = [0] * n
	dfs(0, -1, 0)
	m = 2 * n
	m0 = 2 ** (m - 1).bit_length()
	data = [INF] * (2 * m0)
	for (i, v) in enumerate(euler_tour):
		data[m0 - 1 + i] = (depth[v], i)
	for i in range(m0 - 2, -1, -1):
		data[i] = min(data[2 * i + 1], data[2 * i + 2])
	v_queries = [{} for _ in range(n)]
	lca = []
	for (i, (x, y, u, v)) in enumerate(queries):
		w = query(u, v)
		lca.append(w)
		v_queries[u][x] = [0, 0]
		v_queries[v][x] = [0, 0]
		if w != 0:
			v_queries[w][x] = [0, 0]
	tmp_dists = defaultdict(lambda : [0, 0])
	dfs2(0, -1, tmp_dists, 0)
	ans = []
	for (w, (x, y, u, v)) in zip(lca, queries):
		qu = v_queries[u][x]
		qv = v_queries[v][x]
		du = qu[0] + qu[1] * y
		dv = qv[0] + qv[1] * y
		if w == 0:
			ans.append(du + dv)
			continue
		qw = v_queries[w][x]
		dw = qw[0] + qw[1] * y
		ans.append(du + dv - 2 * dw)
	return ans
(n, q) = list(map(int, input().split()))
links = [set() for _ in range(n)]
lines = sys.stdin.readlines()
for line in lines[:n - 1]:
	(a, b, c, d) = list(map(int, line.split()))
	a -= 1
	b -= 1
	links[a].add((b, c, d))
	links[b].add((a, c, d))
queries = []
for line in lines[n - 1:]:
	(x, y, u, v) = map(int, line.split())
	u -= 1
	v -= 1
	queries.append((x, y, u, v))
ans = solve(n, links, queries)
print('\n'.join(map(str, ans)))


import sys
sys.setrecursionlimit(10000000)
input = sys.stdin.readline
from math import log
from collections import deque

def solve():
	(N, Q) = list(map(int, input().split()))
	es = [[] for i in range(N + 1)]
	for i in range(N - 1):
		(u, v, c, d) = list(map(int, input().split()))
		es[u].append([v, c, d])
		es[v].append([u, c, d])
	LOG_N = int(log(N, 2)) + 1
	parent = [[-1] * (N + 1) for i in range(LOG_N)]
	level = [None] * (N + 1)
	level[1] = 0
	dq = deque()
	dq.append(1)
	while dq:
		v = dq.popleft()
		p = parent[0][v]
		lv = level[v]
		for (u, c, d) in es[v]:
			if u != p:
				parent[0][u] = v
				level[u] = lv + 1
				dq.append(u)
	for k in range(LOG_N - 1):
		parentk = parent[k]
		for v in range(1, N + 1):
			if parentk[v] < 0:
				parent[k + 1][v] = -1
			else:
				parent[k + 1][v] = parentk[parentk[v]]

	def lca(u, v):
		if level[u] > level[v]:
			(u, v) = (v, u)
		for k in range(LOG_N)[::-1]:
			if level[v] - level[u] >> k & 1:
				v = parent[k][v]
		if u == v:
			return u
		for k in range(LOG_N)[::-1]:
			if parent[k][u] != parent[k][v]:
				(u, v) = (parent[k][u], parent[k][v])
		return parent[0][u]
	qs = [[] for i in range(N + 1)]
	for i in range(Q):
		(x, y, u, v) = list(map(int, input().split()))
		qs[u].append([i, x, y, 1])
		qs[v].append([i, x, y, 1])
		a = lca(u, v)
		qs[a].append([i, x, y, -2])
	ds = [0] * N
	cnt = [0] * N
	query = [0] * Q

	def EulerTour(v, p, d):
		for (id, col, y, f) in qs[v]:
			query[id] += f * (d + y * cnt[col] - ds[col])
		for (u, c, _d) in es[v]:
			if u != p:
				ds[c] += _d
				cnt[c] += 1
				EulerTour(u, v, d + _d)
				ds[c] -= _d
				cnt[c] -= 1
	EulerTour(1, -1, 0)
	for res in query:
		print(res)

def __starting_point():
	solve()
__starting_point()


from bisect import bisect_left, bisect_right
import sys
input = sys.stdin.readline
sys.setrecursionlimit(10 ** 9)

def solve():
	INF = float('inf')
	(N, Q) = map(int, input().split())
	adjL = [[] for _ in range(N)]
	for _ in range(N - 1):
		(A, B, C, D) = map(int, input().split())
		(A, B, C) = (A - 1, B - 1, C - 1)
		adjL[A].append((B, C, D))
		adjL[B].append((A, C, D))
	querys = [tuple(map(int, input().split())) for _ in range(Q)]

	def dfs(vNow, vPar, depth):
		iVFirsts[vNow] = len(vs)
		vs.append(vNow)
		depths.append(depth)
		for (v2, clr, dist) in adjL[vNow]:
			if v2 == vPar:
				continue
			iVColorss[clr].append(len(numEs))
			numEs.append(1)
			sumDs.append(dist)
			dfs(v2, vNow, depth + 1)
			vs.append(vNow)
			depths.append(depth)
			iVColorss[clr].append(len(numEs))
			numEs.append(-1)
			sumDs.append(-dist)
	vs = []
	iVFirsts = [-1] * N
	depths = []
	numEs = [0]
	sumDs = [0]
	iVColorss = [[0] for _ in range(N - 1)]
	dfs(0, -1, 0)
	dists = [0] * N
	accD = 0
	for (i, sumD) in enumerate(sumDs):
		accD += sumD
		if iVFirsts[vs[i]] == i:
			dists[vs[i]] = accD
	for iVColors in iVColorss:
		for i in range(1, len(iVColors)):
			numEs[iVColors[i]] += numEs[iVColors[i - 1]]
			sumDs[iVColors[i]] += sumDs[iVColors[i - 1]]
	idEle = (INF, -1)

	def _binOpe(x, y):
		return x if x <= y else y

	def makeSegTree(numEle):
		numPow2 = 2 ** (numEle - 1).bit_length()
		data = [idEle] * (2 * numPow2)
		return (data, numPow2)

	def setInit(As):
		for (iST, A) in enumerate(As, numPow2):
			data[iST] = A
		for iST in reversed(range(1, numPow2)):
			data[iST] = _binOpe(data[2 * iST], data[2 * iST + 1])

	def getValue(iSt, iEn):
		L = iSt + numPow2
		R = iEn + numPow2
		ans = idEle
		while L < R:
			if L & 1:
				ans = _binOpe(ans, data[L])
				L += 1
			if R & 1:
				R -= 1
				ans = _binOpe(ans, data[R])
			L >>= 1
			R >>= 1
		return ans
	(data, numPow2) = makeSegTree(len(depths))
	setInit([(depth, i) for (i, depth) in enumerate(depths)])

	def getLCA(x, y):
		(L, R) = (iVFirsts[x], iVFirsts[y])
		if L > R:
			(L, R) = (R, L)
		(minV, iMinV) = getValue(L, R)
		return vs[iMinV]
	anss = []
	for (clr, distNew, u, v) in querys:
		(u, v) = (u - 1, v - 1)
		LCA = getLCA(u, v)
		clr -= 1
		ds = [dists[u], dists[v], dists[LCA]]
		for (i, vv) in enumerate([u, v, LCA]):
			iIV = bisect_right(iVColorss[clr], iVFirsts[vv]) - 1
			iV = iVColorss[clr][iIV]
			ds[i] += numEs[iV] * distNew - sumDs[iV]
		ans = ds[0] + ds[1] - 2 * ds[2]
		anss.append(ans)
	print('\n'.join(map(str, anss)))
solve()


from collections import defaultdict, deque, Counter
from heapq import heappush, heappop, heapify
import math
import bisect
import random
from itertools import permutations, accumulate, combinations, product
import sys
from pprint import pprint
from copy import deepcopy
import string
from bisect import bisect_left, bisect_right
from math import factorial, ceil, floor
from operator import mul
from functools import reduce
from pprint import pprint
sys.setrecursionlimit(2147483647)
INF = 10 ** 13

def LI():
	return list(map(int, sys.stdin.buffer.readline().split()))

def I():
	return int(sys.stdin.buffer.readline())

def LS():
	return sys.stdin.buffer.readline().rstrip().decode('utf-8').split()

def S():
	return sys.stdin.buffer.readline().rstrip().decode('utf-8')

def IR(n):
	return [I() for i in range(n)]

def LIR(n):
	return [LI() for i in range(n)]

def SR(n):
	return [S() for i in range(n)]

def LSR(n):
	return [LS() for i in range(n)]

def SRL(n):
	return [list(S()) for i in range(n)]

def MSRL(n):
	return [[int(j) for j in list(S())] for i in range(n)]
mod = 1000000007

class LCA:

	def __init__(self, v, Edges, root=0):
		self.v = v
		self.Edges = Edges
		self.maxLog = 18
		self.Parent = [[-1] * v for _ in range(self.maxLog + 1)]
		self.Depth = [0] * v
		self.__bfs(root)
		for i in range(self.maxLog):
			for j in range(v):
				if self.Parent[i][j] != -1:
					self.Parent[i + 1][j] = self.Parent[i][self.Parent[i][j]]

	def __bfs(self, root):
		Visited = [False] * self.v
		Visited[root] = True
		q = deque([root])
		while q:
			fr = q.pop()
			for to in self.Edges[fr]:
				if Visited[to]:
					continue
				self.Parent[0][to] = fr
				self.Depth[to] = self.Depth[fr] + 1
				Visited[to] = True
				q.append(to)

	def lca(self, a, b):
		if self.Depth[a] > self.Depth[b]:
			(a, b) = (b, a)
		for i in range(self.maxLog):
			if self.Depth[b] - self.Depth[a] & 1 << i:
				b = self.Parent[i][b]
		if a == b:
			return b
		for i in reversed(range(self.maxLog - 1)):
			if self.Parent[i][a] != self.Parent[i][b]:
				a = self.Parent[i][a]
				b = self.Parent[i][b]
		return self.Parent[0][a]
(n, q) = LI()
edge_dict = [{} for _ in range(n)]
for (u, v, c, d) in LIR(n - 1):
	edge_dict[u - 1][v - 1] = (c, d)
	edge_dict[v - 1][u - 1] = (c, d)
lca = LCA(n, edge_dict)
query = [[] for _ in range(n)]
for (q_idx, (x, y, u, v)) in enumerate(LIR(q)):
	query[u - 1] += [(q_idx, x, y, 1)]
	query[v - 1] += [(q_idx, x, y, 1)]
	query[lca.lca(u - 1, v - 1)] += [(q_idx, x, y, 0)]

def euler_tour(G, root=0):
	n = len(G)
	euler = []
	depth = [-1] * n
	depth[root] = 0
	dq = deque([root])
	dq2 = deque()
	visited = [0] * n
	while dq:
		u = dq.pop()
		euler += [(depth[u], u)]
		if visited[u]:
			continue
		for v in G[u]:
			if visited[v]:
				dq += [v]
			else:
				depth[v] = depth[u] + 1
				dq2 += [v]
		dq.extend(dq2)
		dq2 = deque()
		visited[u] = 1
	return euler
dist = 0
color_len = [0] * n
color_cnt = [0] * n
ans = [0] * q
euler = euler_tour(edge_dict)
for i in range(1, len(euler)):
	dep = euler[i][0]
	(c, d) = edge_dict[euler[i - 1][1]][euler[i][1]]
	if euler[i - 1][0] < euler[i][0]:
		dist += d
		color_len[c] += d
		color_cnt[c] += 1
		for (q_idx, x, y, flag) in query[euler[i][1]]:
			if flag:
				ans[q_idx] += dist - color_len[x] + color_cnt[x] * y
			else:
				ans[q_idx] -= (dist - color_len[x] + color_cnt[x] * y) * 2
	else:
		dist -= d
		color_len[c] -= d
		color_cnt[c] -= 1
print(*ans, sep='\n')


import sys
sys.setrecursionlimit(10 ** 6 + 1)

def input():
	x = sys.stdin.readline()
	return x[:-1] if x[-1] == '\n' else x

def printe(*x):
	print('## ', *x, file=sys.stderr)

def printl(li):
	_ = print(*li, sep='\n') if li else None
(N, Q) = map(int, input().split())
edge = [[] for i in range(N)]
for i in range(N - 1):
	(a, b, c, w) = map(int, input().split())
	edge[a - 1].append((b - 1, c - 1, w))
	edge[b - 1].append((a - 1, c - 1, w))

def dfs(start):
	q = [(start, -1, 0, 0)]
	pars = [-1] * N
	depth = [-1] * N
	dist = [-1] * N
	while len(q):
		(e, p, d, dis) = q.pop()
		if depth[e] != -1:
			continue
		pars[e] = p
		depth[e] = d
		dist[e] = dis
		for (ne, c, w) in edge[e]:
			q.append((ne, e, d + 1, dis + w))
	return (pars, depth, dist)
(pars, d, dist) = dfs(0)
ln = N.bit_length()
dp = [[-1] * N for _ in range(ln + 1)]
dp[0] = pars
for i in range(1, ln + 1):
	for j in range(N):
		dp[i][j] = dp[i - 1][dp[i - 1][j]]

def lca(u, v):
	du = d[u]
	dv = d[v]
	if du > dv:
		(du, dv) = (dv, du)
		(u, v) = (v, u)
	dif = dv - du
	for i in range(ln + 1):
		if dif >> i & 1:
			v = dp[i][v]
	if u == v:
		return u
	for i in range(ln, -1, -1):
		pu = dp[i][u]
		pv = dp[i][v]
		if pu != pv:
			(u, v) = (pu, pv)
	return pars[u]
q = Q
n = N
A = [0] * q
C = [[] for _ in range(n)]
num = [0] * n
cum = [0] * n
for i in range(q):
	(x, y, u, v) = map(int, input().split())
	u -= 1
	v -= 1
	a = lca(u, v)
	C[u].append((i, x, y, 1))
	C[v].append((i, x, y, 1))
	C[a].append((i, x, y, -2))
dist = 0
stack = [0]

def dfs2(v):
	global dist
	for (i, x, y, b) in C[v]:
		A[i] += (dist + num[x] * y - cum[x]) * b
	for (nv, c, d) in edge[v]:
		c += 1
		if nv == pars[v]:
			continue
		dist += d
		num[c] += 1
		cum[c] += d
		dfs2(nv)
		dist -= d
		num[c] -= 1
		cum[c] -= d
dfs2(0)
print(*A, sep='\n')


from collections import deque, defaultdict
import sys
sys.setrecursionlimit(10 ** 7)
(N, Q) = map(int, input().split())
table = [[] for i in range(N)]
ddcost = defaultdict(tuple)
for i in range(N - 1):
	(a, b, c, d) = map(int, input().split())
	(a, b, c) = (a - 1, b - 1, c - 1)
	table[a].append(b)
	table[b].append(a)
	ddcost[min(a, b), max(b, a)] = (c, d)
H = deque()
H.append(0)
visit = [-1] * N
visit[0] = 0
parent = [[-1] * N for i in range(18)]
while H:
	x = H.popleft()
	for y in table[x]:
		if visit[y] != -1:
			continue
		parent[0][y] = x
		visit[y] = visit[x] + 1
		H.append(y)
for k in range(17):
	for i in range(N):
		if parent[k][i] < 0:
			parent[k + 1][i] = -1
		else:
			parent[k + 1][i] = parent[k][parent[k][i]]

def lca(u, v):
	if visit[u] > visit[v]:
		(u, v) = (v, u)
	for k in range(18):
		if visit[v] - visit[u] >> k & 1:
			v = parent[k][v]
	if u == v:
		return u
	for k in range(17, -1, -1):
		if parent[k][u] != parent[k][v]:
			u = parent[k][u]
			v = parent[k][v]
	return parent[0][u]
query = []
sakidori = [set() for i in range(N)]
for i in range(Q):
	(x, y, u, v) = map(int, input().split())
	(x, u, v) = (x - 1, u - 1, v - 1)
	if u >= v:
		(u, v) = (v, u)
	pa = lca(u, v)
	sakidori[u].add(x)
	sakidori[v].add(x)
	sakidori[pa].add(x)
	query.append((x, y, u, v, pa))
ddcolorsum = defaultdict(int)
ddcolornum = defaultdict(int)
W = deque()
colorsum = [0] * N
colornum = [0] * N
dist = [0] * N
vi = [0] * N
W.append((0, 0, 1))
while W:
	(pa, ci, vector) = W.pop()
	if vector == -1:
		(color, dis) = ddcost[min(pa, ci), max(pa, ci)]
		colorsum[color] -= dis
		colornum[color] -= 1
		continue
	if pa == 0 and ci == 0:
		for y in table[ci]:
			W.append((ci, y, 1))
		for co in sakidori[0]:
			ddcolorsum[0, co] = 0
			ddcolornum[0, co] = 0
		continue
	(color, dis) = ddcost[min(pa, ci), max(pa, ci)]
	dist[ci] = dist[pa] + dis
	colorsum[color] += dis
	colornum[color] += 1
	for co in sakidori[ci]:
		ddcolorsum[ci, co] = colorsum[co]
		ddcolornum[ci, co] = colornum[co]
	W.append((pa, ci, -1))
	for y in table[ci]:
		if y == pa:
			continue
		W.append((ci, y, 1))

def dista(x, y, u):
	return dist[u] - ddcolorsum[u, x] + y * ddcolornum[u, x]
for (x, y, u, v, pa) in query:
	print(dista(x, y, u) + dista(x, y, v) - 2 * dista(x, y, pa))


import sys
input = sys.stdin.readline
sys.setrecursionlimit(10 ** 5)
(n, q) = map(int, input().split())
edges = [[] for i in range(n)]
for i in range(n - 1):
	(a, b, c, d) = map(int, input().split())
	a -= 1
	b -= 1
	c -= 1
	edges[a].append((b, c, d))
	edges[b].append((a, c, d))
dep = [0] * n
par = [[-1] * n for i in range(18)]
dis = [0] * n

def dfs(p, v, d, dd):
	dep[v] = d
	par[0][v] = p
	dis[v] = dd
	for (w, _, dist) in edges[v]:
		if w != p:
			dfs(v, w, d + 1, dd + dist)

def lca(a, b):
	if dep[a] > dep[b]:
		(a, b) = (b, a)
	for i in range(18):
		if dep[b] - dep[a] & 1 << i:
			b = par[i][b]
	if a == b:
		return a
	for i in range(18)[::-1]:
		if par[i][a] != par[i][b]:
			a = par[i][a]
			b = par[i][b]
	return par[0][a]
dfs(-1, 0, 0, 0)
for i in range(17):
	for j in range(n):
		par[i + 1][j] = par[i][par[i][j]]
ans = [0] * q
v_need = [[] for i in range(n)]
for i in range(q):
	(x, y, u, v) = map(int, input().split())
	x -= 1
	u -= 1
	v -= 1
	l = lca(u, v)
	v_need[u].append((i, x, y, 1))
	v_need[v].append((i, x, y, 1))
	v_need[l].append((i, x, y, -2))
color = [0] * n
cdist = [0] * n

def dfs2(cur, par):
	for (i, x, y, k) in v_need[cur]:
		ans[i] += k * (dis[cur] - cdist[x] + y * color[x])
	for (to, x, y) in edges[cur]:
		if to == par:
			continue
		color[x] += 1
		cdist[x] += y
		dfs2(to, cur)
		color[x] -= 1
		cdist[x] -= y
dfs2(0, -1)
for i in ans:
	print(i)


import sys
sys.setrecursionlimit(10 ** 7)

def main0(n, g, xyuv):
	depth = [0] * n
	tour = []
	hatsu = [0] * n
	dist = [0] * n
	tmp = 0

	def et(p, v, tour, tmp, pc, pd):
		hatsu[v] = len(tour) - 1
		for (nv, c, d) in g[v]:
			if nv == p:
				continue
			depth[nv] = depth[v] + 1
			tmp += d
			dist[nv] = tmp
			tour.append((nv, c, d))
			tour = et(v, nv, tour, tmp, c, d)
			tmp -= d
			tour.append((v, c, -d))
		return tour
	tour.append((0, 0, 0))
	tour = et(-1, 0, tour, 0, 0, 0)

	class SegmentTree:

		def __init__(self, size, f=lambda x, y: x + y, default=0):
			self.size = pow(2, (size - 1).bit_length())
			self.f = f
			self.default = default
			self.data = [default] * (self.size * 2)

		def update(self, i, x):
			i += self.size
			self.data[i] = x
			while i:
				i >>= 1
				self.data[i] = self.f(self.data[i * 2], self.data[i * 2 + 1])

		def query(self, l, r):
			(l, r) = (l + self.size, r + self.size)
			(lret, rret) = (self.default, self.default)
			while l < r:
				if l & 1:
					lret = self.f(self.data[l], lret)
					l += 1
				if r & 1:
					r -= 1
					rret = self.f(self.data[r], rret)
				l >>= 1
				r >>= 1
			return self.f(lret, rret)

	def func(x, y):
		if x[0] < y[0]:
			return x
		return y
	st = SegmentTree(len(tour), func, (n + 1, 0))
	for (i, x) in enumerate(tour):
		v = x[0]
		st.update(i, (depth[v], v))

	def lca(u, v):
		(x, y) = (hatsu[u], hatsu[v])
		if x < y:
			y += 1
		else:
			(x, y) = (y, x)
			y += 1
		return st.query(x, y)[1]
	dc = {}
	dcary = {}
	for (i, (v, c, d)) in enumerate(tour):
		if c in dc:
			ci = 1 if d > 0 else -1
			dc[c].append((dc[c][-1][0] + ci, dc[c][-1][1] + d))
			dcary[c].append(i)
		else:
			dc[c] = [(0, 0), (1, d)]
			dcary[c] = [0, i]
	ret = []
	from bisect import bisect_right as bl
	for (x, y, u, v) in xyuv:
		(u, v) = (u - 1, v - 1)
		w = lca(u, v)
		uvlen = dist[u] + dist[v] - dist[w] * 2
		if x not in dc:
			ret.append(uvlen)
			continue
		widx = bl(dcary[x], hatsu[w]) - 1
		(wnum, wsum) = dc[x][widx]
		uidx = bl(dcary[x], hatsu[u]) - 1
		vidx = bl(dcary[x], hatsu[v]) - 1
		(unum, usum) = dc[x][uidx]
		(vnum, vsum) = dc[x][vidx]
		uvsum = usum + vsum - wsum * 2
		uvnum = unum + vnum - wnum * 2
		ret.append(uvlen - uvsum + uvnum * y)
	return ret

def __starting_point():
	(n, q) = map(int, input().split())
	abcd = [list(map(int, input().split())) for _ in range(n - 1)]
	xyuv = [list(map(int, input().split())) for _ in range(q)]
	g = [[] for _ in range(n)]
	for (a, b, c, d) in abcd:
		(a, b) = (a - 1, b - 1)
		g[a].append((b, c, d))
		g[b].append((a, c, d))
	ary0 = main0(n, g, xyuv)
	print(*ary0, sep='\n')
__starting_point()


import sys
sys.setrecursionlimit(10 ** 5 + 5)
from collections import defaultdict, deque
input = sys.stdin.readline

class LCA:

	def __init__(self, n):
		self.size = n + 1
		self.bitlen = n.bit_length()
		self.lca = [[0] * self.size for i in range(self.bitlen)]
		self.depth = [-1] * self.size
		self.dis = [-1] * self.size
		self.depth[1] = 0
		self.dis[1] = 0

	def make(self, root):
		q = deque([root])
		while q:
			now = q.popleft()
			for (nex, c, w) in e[now]:
				if self.depth[nex] >= 0:
					continue
				self.depth[nex] = self.depth[now] + 1
				self.dis[nex] = self.dis[now] + w
				self.lca[0][nex] = now
				q.append(nex)
		for i in range(1, self.bitlen):
			for j in range(self.size):
				if self.lca[i - 1][j] > 0:
					self.lca[i][j] = self.lca[i - 1][self.lca[i - 1][j]]

	def search(self, x, y):
		dx = self.depth[x]
		dy = self.depth[y]
		if dx < dy:
			(x, y) = (y, x)
			(dx, dy) = (dy, dx)
		dif = dx - dy
		while dif:
			s = dif & -dif
			x = self.lca[s.bit_length() - 1][x]
			dif -= s
		if x == y:
			return x
		for i in range(self.bitlen - 1, -1, -1):
			if self.lca[i][x] != self.lca[i][y]:
				x = self.lca[i][x]
				y = self.lca[i][y]
		return self.lca[0][x]
(n, q) = map(int, input().split())
e = [[] for i in range(n + 1)]
for i in range(n - 1):
	(a, b, c, d) = map(int, input().split())
	e[a].append((b, c, d))
	e[b].append((a, c, d))
lca = LCA(n)
lca.make(1)
Q = []
dcount = defaultdict(lambda : defaultdict(int))
dweight = defaultdict(lambda : defaultdict(int))
for i in range(q):
	(x, y, u, v) = map(int, input().split())
	a = lca.search(u, v)
	dcount[u][x] = 0
	dcount[v][x] = 0
	dcount[a][x] = 0
	dweight[u][x] = 0
	dweight[v][x] = 0
	dweight[a][x] = 0
	Q.append((x, y, u, v, a))
dis = [0] * (n + 1)

def dfs1(now, p, count1, count2):
	for cou in dcount[now]:
		dcount[now][cou] = count1[cou]
	for wei in dweight[now]:
		dweight[now][wei] = count2[wei]
	for (nex, c, w) in e[now]:
		if nex == p:
			continue
		count1[c] += 1
		count2[c] += w
		dis[nex] = dis[now] + w
		dfs1(nex, now, count1, count2)
		count1[c] -= 1
		count2[c] -= w
dfs1(1, 0, defaultdict(int), defaultdict(int))
for (x, y, u, v, a) in Q:
	cal = dis[u] + dis[v] - 2 * dis[a]
	cal += y * (dcount[u][x] + dcount[v][x] - 2 * dcount[a][x]) - dweight[u][x] - dweight[v][x] + 2 * dweight[a][x]
	print(cal)


import sys
input = sys.stdin.readline
sys.setrecursionlimit(10 ** 5)
(N, Q) = map(int, input().split())
path = [[] for _ in range(N)]
for _ in range(N - 1):
	(a, b, c, d) = (int(i) for i in input().split())
	path[a - 1].append((b - 1, c - 1, d))
	path[b - 1].append((a - 1, c - 1, d))
for K in range(18):
	if 2 ** K >= N:
		break
parent = [[-1] * N for _ in range(K)]
rank = [-1 for _ in range(N)]
rank[0] = 0
queue = [0]
while queue:
	cur = queue.pop()
	for (nex, _, _) in path[cur]:
		if rank[nex] < 0:
			queue.append(nex)
			parent[0][nex] = cur
			rank[nex] = rank[cur] + 1
for i in range(1, K):
	for j in range(N):
		if parent[i - 1][j] > 0:
			parent[i][j] = parent[i - 1][parent[i - 1][j]]

def lca(a, b):
	if rank[a] > rank[b]:
		(a, b) = (b, a)
	diff = rank[b] - rank[a]
	i = 0
	while diff > 0:
		if diff & 1:
			b = parent[i][b]
		diff >>= 1
		i += 1
	if a == b:
		return a
	for i in range(K - 1, -1, -1):
		if parent[i][a] != parent[i][b]:
			a = parent[i][a]
			b = parent[i][b]
	return parent[0][a]
schedule = [[] for _ in range(N)]
for i in range(Q):
	(x, y, u, v) = map(int, input().split())
	(x, u, v) = (x - 1, u - 1, v - 1)
	l = lca(u, v)
	schedule[u].append((i, 1, x, y))
	schedule[v].append((i, 1, x, y))
	schedule[l].append((i, -2, x, y))
ret = [0] * Q
C = [0] * (N - 1)
D = [0] * (N - 1)

def dfs(cur, pre, tot):
	for (i, t, c, d) in schedule[cur]:
		ret[i] += t * (tot - D[c] + C[c] * d)
	for (nex, c, d) in path[cur]:
		if nex == pre:
			continue
		C[c] += 1
		D[c] += d
		dfs(nex, cur, tot + d)
		C[c] -= 1
		D[c] -= d
dfs(0, -1, 0)
for i in range(Q):
	print(ret[i])


import bisect
import operator
import os
import sys
from collections import defaultdict
if os.getenv('LOCAL'):
	sys.stdin = open('_in.txt', 'r')
sys.setrecursionlimit(2147483647)
INF = float('inf')
IINF = 10 ** 18
MOD = 10 ** 9 + 7

class SegmentTree:

	def __init__(self, size, fn=operator.add, default=None, initial_values=None):
		default = default or 0
		n = 1
		while n < size:
			n *= 2
		self._size = n
		self._fn = fn
		self._tree = [default] * (self._size * 2 - 1)
		if initial_values:
			i = self._size - 1
			for v in initial_values:
				self._tree[i] = v
				i += 1
			i = self._size - 2
			while i >= 0:
				self._tree[i] = self._fn(self._tree[i * 2 + 1], self._tree[i * 2 + 2])
				i -= 1

	def set(self, i, value):
		x = self._size - 1 + i
		self._tree[x] = value
		while x > 0:
			x = (x - 1) // 2
			self._tree[x] = self._fn(self._tree[x * 2 + 1], self._tree[x * 2 + 2])

	def add(self, i, value):
		x = self._size - 1 + i
		self.set(i, self._fn(self._tree[x], value))

	def get(self, from_i, to_i, k=0, L=None, r=None):
		L = 0 if L is None else L
		r = self._size if r is None else r
		if from_i <= L and r <= to_i:
			return self._tree[k]
		if to_i <= L or r <= from_i:
			return None
		ret_L = self.get(from_i, to_i, k * 2 + 1, L, (L + r) // 2)
		ret_r = self.get(from_i, to_i, k * 2 + 2, (L + r) // 2, r)
		if ret_L is None:
			return ret_r
		if ret_r is None:
			return ret_L
		return self._fn(ret_L, ret_r)

	def __len__(self):
		return self._size

def eulerian_trail(tree, max_v, root=0):
	seen = [False] * (max_v + 1)
	trails = []
	depths = []
	edges = []
	stack = [(root, 0, (0, 0), True)]
	while stack:
		(v, d, edge, forward) = stack.pop()
		seen[v] = True
		trails.append(v)
		depths.append(d)
		edges.append(edge)
		if not forward:
			continue
		for (u, c, w) in tree[v]:
			if not seen[u]:
				stack.append((v, d, (c, -w), False))
				stack.append((u, d + 1, (c, w), True))
	return (trails, depths, edges)
(N, Q) = list(map(int, sys.stdin.readline().split()))
ABCD = [list(map(int, sys.stdin.readline().split())) for _ in range(N - 1)]
XYVU = [list(map(int, sys.stdin.readline().split())) for _ in range(Q)]
graph = [[] for _ in range(N + 1)]
for (a, b, c, d) in ABCD:
	graph[a].append((b, c, d))
	graph[b].append((a, c, d))
(trails, depths, edges) = eulerian_trail(graph, max_v=N, root=1)
ids = [-1] * (N + 1)
for (i, t) in enumerate(trails):
	ids[t] = i
distances = []
color_dist = defaultdict(lambda : [(-1, 0)])
color_cnt = defaultdict(lambda : [(-1, 0)])
s = 0
for (i, (c, d)) in enumerate(edges):
	s += d
	distances.append(s)
	color_dist[c].append((i, color_dist[c][-1][1] + d))
	(_, cc) = color_cnt[c][-1]
	color_cnt[c].append((i, cc + 1 if d > 0 else cc - 1))
st = SegmentTree(size=len(trails), fn=min, default=(IINF, -1), initial_values=list(zip(depths, trails)))

def get_lca(v, u):
	(iv, iu) = (ids[v], ids[u])
	if iv > iu:
		(iv, iu) = (iu, iv)
	(_, ret) = st.get(iv, iu + 1)
	return ret

def distance(v):
	return distances[ids[v]]

def count_color(v, c):
	iv = ids[v]
	i = bisect.bisect_left(color_cnt[c], (iv + 1, 0))
	return color_cnt[c][i - 1][1]

def distance_color(v, c):
	iv = ids[v]
	i = bisect.bisect_left(color_dist[c], (iv + 1, 0))
	return color_dist[c][i - 1][1]
for (x, y, u, v) in XYVU:
	lca = get_lca(u, v)
	d = distance(u) + distance(v) - distance(lca) * 2
	c = count_color(u, x) + count_color(v, x) - count_color(lca, x) * 2
	s = distance_color(u, x) + distance_color(v, x) - distance_color(lca, x) * 2
	print(d - s + c * y)


import sys
from collections import defaultdict
readline = sys.stdin.readline
(N, Q) = map(int, readline().split())
vs = set()

def dfs_w(v, d):
	(S, F, depth, depthh) = ([], [None] + [0] * N, [-1] + [0] * N, [0] + [0] * N)
	(mmn, msn, iin) = ([[0] for _ in range(N)], [[0] for _ in range(N)], [[0] for _ in range(N)])
	(pvs, stack, path) = (set(), [(1, 0, 0)], [0])
	while True:
		if len(stack) == 0:
			break
		(v, cc, dpd) = stack.pop()
		if v > 0:
			parent = path[-1]
			path.append(v)
			pvs.add(v)
			vs.add(v)
			F[v] = len(S)
			(depth[v], depthh[v]) = (depth[parent] + 1, depthh[parent] + dpd)
			mmn[cc].append(mmn[cc][-1] + 1)
			msn[cc].append(msn[cc][-1] + dpd)
			iin[cc].append(len(S))
			S.append(v)
			for (u, ucc, udpd) in d[v]:
				if u in pvs:
					continue
					for (i, c) in enumerate(path):
						if c == u:
							break
					return path[i + 1:] + [u]
				if u in vs:
					continue
				stack += [(-v, ucc, udpd), (u, ucc, udpd)]
		else:
			pvs.remove(path[-1])
			path.pop()
			mmn[cc].append(mmn[cc][-1] - 1)
			msn[cc].append(msn[cc][-1] - dpd)
			iin[cc].append(len(S))
			S.append(v)
	return (S, F, depth, depthh, mmn, msn, iin)

class SegTree:

	def __init__(self, init_val, n, ide_ele, seg_func):
		self.segfunc = seg_func
		self.num = 2 ** (n - 1).bit_length()
		self.ide_ele = ide_ele
		self.seg = [self.ide_ele] * 2 * self.num
		for i in range(n):
			self.seg[i + self.num - 1] = init_val[i]
		for i in range(self.num - 2, -1, -1):
			self.seg[i] = self.segfunc(self.seg[2 * i + 1], self.seg[2 * i + 2])

	def update(self, k, x):
		k += self.num - 1
		self.seg[k] = x
		while k + 1:
			k = (k - 1) // 2
			self.seg[k] = self.segfunc(self.seg[k * 2 + 1], self.seg[k * 2 + 2])

	def query(self, p, q):
		if q <= p:
			return self.ide_ele
		p += self.num - 1
		q += self.num - 2
		res = self.ide_ele
		while q - p > 1:
			if p & 1 == 0:
				res = self.segfunc(res, self.seg[p])
			if q & 1 == 1:
				res = self.segfunc(res, self.seg[q])
				q -= 1
			p = p // 2
			q = (q - 1) // 2
		if p == q:
			res = self.segfunc(res, self.seg[p])
		else:
			res = self.segfunc(self.segfunc(res, self.seg[p]), self.seg[q])
		return res

def bsearch(ll, target, min_i, max_i):
	if ll[max_i] <= target:
		return max_i
	if target < ll[min_i]:
		return None
	index = len(ll) // 2
	while True:
		if ll[index] <= target:
			if target < ll[index + 1]:
				return index
			(index, min_i) = ((index + 1 + max_i) // 2, index + 1)
			continue
		(index, max_i) = ((index - 1 + min_i) // 2, index - 1)

def process():
	G = [set() for _ in range(N + 1)]
	for _ in range(N - 1):
		(a, b, c, d) = map(int, readline().split())
		G[a].add((b, c, d))
		G[b].add((a, c, d))
	(S, F, depth, depthh, mmn, msn, iin) = dfs_w(1, G)
	stree = SegTree([(depth[abs(v)], i) for (i, v) in enumerate(S)], len(S), (N, None), min)
	for _ in range(Q):
		(x, y, u, v) = map(int, readline().split())
		(fu, fv) = sorted([F[u], F[v]])
		cc = abs(S[stree.query(fu, fv + 1)[1]])
		r = depthh[u] + depthh[v] - 2 * depthh[cc]
		index = bsearch(iin[x], F[u], 0, len(iin[x]) - 1)
		r += mmn[x][index] * y - msn[x][index]
		index = bsearch(iin[x], F[v], 0, len(iin[x]) - 1)
		r += mmn[x][index] * y - msn[x][index]
		index = bsearch(iin[x], F[cc], 0, len(iin[x]) - 1)
		r -= 2 * (mmn[x][index] * y - msn[x][index])
		print(r)
process()


import bisect
from collections import deque

class LowestCommonAncestor:

	def __init__(self, tree, root):
		self.n = len(tree)
		self.depth = [0] * self.n
		self.log_size = self.n.bit_length()
		self.parent = [[-1] * self.n for i in range(self.log_size)]
		q = deque([(root, -1, 0)])
		while q:
			(v, par, dist) = q.pop()
			self.parent[0][v] = par
			self.depth[v] = dist
			for (child_v, _, _) in tree[v]:
				if child_v != par:
					self.depth[child_v] = dist + 1
					q.append((child_v, v, dist + 1))
		for k in range(1, self.log_size):
			for v in range(self.n):
				self.parent[k][v] = self.parent[k - 1][self.parent[k - 1][v]]

	def lca(self, u, v):
		if self.depth[u] > self.depth[v]:
			(u, v) = (v, u)
		for k in range(self.log_size):
			if self.depth[v] - self.depth[u] >> k & 1:
				v = self.parent[k][v]
		if u == v:
			return u
		for k in reversed(range(self.log_size)):
			if self.parent[k][u] != self.parent[k][v]:
				u = self.parent[k][u]
				v = self.parent[k][v]
		return self.parent[0][u]

def euler_tour(tree: list, root: int):
	n = len(tree)
	trail = []
	weight = []
	color = []
	begin = [-1] * n
	end = [-1] * n
	par = {root: -1}
	stack = [(root, 0, 0)]
	while stack:
		(v, c, col) = stack.pop()
		if v >= 0:
			trail.append(v)
			weight.append(c)
			color.append(col)
			begin[v] = len(trail) - 1
			end[v] = len(trail)
			stack.append((~v, -c, col))
			for (nxt_v, nxt_c, nxt_col) in tree[v]:
				if nxt_v not in par:
					par[nxt_v] = v
					stack.append((nxt_v, nxt_c, nxt_col))
		else:
			if ~v == root:
				continue
			trail.append(par[~v])
			weight.append(c)
			color.append(col)
			end[par[~v]] = len(trail)
	return (trail, weight, color, begin, end)
(n, q) = map(int, input().split())
info = [list(map(int, input().split())) for i in range(n - 1)]
query = [list(map(int, input().split())) for i in range(q)]
tree = [[] for i in range(n)]
for (a, b, col, dist) in info:
	a -= 1
	b -= 1
	tree[a].append((b, dist, col))
	tree[b].append((a, dist, col))
(trail, weight, color, begin, end) = euler_tour(tree, 0)
w_col = [[0] for i in range(n)]
for i in range(len(weight)):
	col = color[i]
	w_col[col].append(weight[i] + w_col[col][-1])
cnt_col = [[0] for i in range(n)]
for i in range(len(weight)):
	col = color[i]
	cnt_col[col].append(int(2 * (weight[i] >= 1)) - 1 + cnt_col[col][-1])
ind = [[] for i in range(n)]
for i in range(len(weight)):
	col = color[i]
	ind[col].append(i)
for i in range(len(weight) - 1):
	weight[i + 1] += weight[i]
lca = LowestCommonAncestor(tree, 0)
for (x, y, u, v) in query:
	u -= 1
	v -= 1
	lca_uv = lca.lca(u, v)
	ans = weight[begin[u]] + weight[begin[v]] - 2 * weight[begin[lca_uv]]
	l = bisect.bisect_right(ind[x], begin[u])
	r = bisect.bisect_right(ind[x], begin[v])
	mid = bisect.bisect_right(ind[x], begin[lca_uv])
	cnt = cnt_col[x][r] + cnt_col[x][l] - 2 * cnt_col[x][mid]
	w = w_col[x][r] + w_col[x][l] - 2 * w_col[x][mid]
	print(abs(ans) + abs(cnt) * y - abs(w))


import sys
sys.setrecursionlimit(10 ** 6)
input = sys.stdin.readline
INF = 10 ** 6

class RMQ:

	def __init__(self, a):
		self.n = len(a)
		self.size = 2 ** (self.n - 1).bit_length()
		self.data = [(INF, INF) for _ in range(2 * self.size - 1)]
		self.initialize(a)

	def initialize(self, a):
		for i in range(self.n):
			self.data[self.size + i - 1] = a[i][:]
		for i in range(self.size - 2, -1, -1):
			self.data[i] = min(self.data[i * 2 + 1], self.data[i * 2 + 2])[:]

	def update(self, k, x):
		k += self.size - 1
		self.data[k] = x
		while k > 0:
			k = (k - 1) // 2
			self.data[k] = min(self.data[2 * k + 1], self.data[2 * k + 2])[:]

	def query(self, l, r):
		L = l + self.size
		R = r + self.size
		s = (INF, INF)
		while L < R:
			if R & 1:
				R -= 1
				s = min(s, self.data[R - 1])[:]
			if L & 1:
				s = min(s, self.data[L - 1])[:]
				L += 1
			L >>= 1
			R >>= 1
		return s

class LCA:

	def __init__(self, edge, root):
		self.itr = 0
		self.edge = edge[:]
		self.path = [0] * (2 * n - 1)
		self.depth = [0] * (2 * n - 1)
		self.color = [0] * (2 * n - 1)
		self.weight = [0] * (2 * n - 1)
		self.index = [0] * n
		self.euler_tour(-1, root, 0, 0, 0, 0)
		dat = list(zip(self.depth, self.path))
		self.rmq = RMQ(dat)

	def get_lca(self, u, v):
		(l, r) = (self.index[u], self.index[v])
		if l > r:
			(l, r) = (r, l)
		return self.rmq.query(l, r + 1)[1]

	def euler_tour(self, prev, v, d, k, c, w):
		self.index[v] = k
		self.path[self.itr] = v
		self.depth[self.itr] = d
		self.color[self.itr] = c
		self.weight[self.itr] = w
		self.itr += 1
		k += 1
		for (dest, c, w) in self.edge[v]:
			if prev == dest:
				continue
			k = self.euler_tour(v, dest, d + 1, k, c, w)
			self.path[self.itr] = v
			self.depth[self.itr] = d
			self.color[self.itr] = -c
			self.weight[self.itr] = -w
			self.itr += 1
			k += 1
		return k
(n, q) = [int(item) for item in input().split()]
edge = [[] for _ in range(n)]
for _ in range(n - 1):
	(u, v, c, w) = [int(item) for item in input().split()]
	u -= 1
	v -= 1
	edge[u].append((v, c, w))
	edge[v].append((u, c, w))
lca = LCA(edge, 0)
memo = [dict() for _ in range(n)]
lca_memo = dict()
query = []
for _ in range(q):
	(x, y, u, v) = [int(item) for item in input().split()]
	u -= 1
	v -= 1
	if u > v:
		(u, v) = (v, u)
	if (u, v) in lca_memo:
		ca = lca_memo[u, v]
	else:
		ca = lca.get_lca(u, v)
		lca_memo[u, v] = ca
	memo[u][x] = None
	memo[v][x] = None
	memo[ca][x] = None
	query.append((x, y, ca, u, v))
colors = [0] * n
c_depth = [0] * n
depth = 0
for (p, c, w) in zip(lca.path[1:], lca.color[1:], lca.weight[1:]):
	color = abs(c)
	if c < 0:
		colors[color] -= 1
	else:
		colors[color] += 1
	c_depth[color] += w
	depth += w
	for key in memo[p].keys():
		if memo[p][key] != None:
			break
		memo[p][key] = (colors[key], c_depth[key])
	memo[p][0] = depth
ans = []
for (x, y, ca, u, v) in query:
	val = memo[u][0] + memo[v][0] - memo[ca][0] * 2
	val += (memo[v][x][0] + memo[u][x][0] - memo[ca][x][0] * 2) * y
	val -= memo[v][x][1] + memo[u][x][1] - memo[ca][x][1] * 2
	ans.append(val)
print('\n'.join(map(str, ans)))


class Tree:

	def __init__(self, n, edge):
		self.n = n
		self.tree = [[] for _ in range(n)]
		for e in edge:
			self.tree[e[0] - 1].append((e[1] - 1, e[2] - 1, e[3]))
			self.tree[e[1] - 1].append((e[0] - 1, e[2] - 1, e[3]))

	def setroot(self, root):
		self.root = root
		self.parent = [None for _ in range(self.n)]
		self.parent[root] = -1
		self.depth = [None for _ in range(self.n)]
		self.depth[root] = 0
		self.distance = [None for _ in range(self.n)]
		self.distance[root] = 0
		self.order = []
		self.order.append(root)
		self.color = [None for _ in range(self.n)]
		self.cost = [0 for _ in range(self.n)]
		self.size = [1 for _ in range(self.n)]
		stack = [root]
		while stack:
			node = stack.pop()
			for (adj, color, cost) in self.tree[node]:
				if self.parent[adj] is None:
					self.parent[adj] = node
					self.depth[adj] = self.depth[node] + 1
					self.distance[adj] = self.distance[node] + cost
					self.color[adj] = color
					self.cost[adj] = cost
					self.order.append(adj)
					stack.append(adj)
		for node in self.order[::-1]:
			for (adj, color, cost) in self.tree[node]:
				if self.parent[node] == adj:
					continue
				self.size[node] += self.size[adj]

	def heavylight_decomposition(self):
		self.order = [None for _ in range(self.n)]
		self.head = [None for _ in range(self.n)]
		self.head[self.root] = self.root
		self.next = [None for _ in range(self.n)]
		stack = [self.root]
		order = 0
		while stack:
			node = stack.pop()
			self.order[node] = order
			order += 1
			maxsize = 0
			for (adj, color, cost) in self.tree[node]:
				if self.parent[node] == adj:
					continue
				if maxsize < self.size[adj]:
					maxsize = self.size[adj]
					self.next[node] = adj
			for (adj, color, cost) in self.tree[node]:
				if self.parent[node] == adj or self.next[node] == adj:
					continue
				self.head[adj] = adj
				stack.append(adj)
			if self.next[node] is not None:
				self.head[self.next[node]] = self.head[node]
				stack.append(self.next[node])

	def range_hld(self, u, v, edge=False):
		res = []
		while True:
			if self.order[u] > self.order[v]:
				(u, v) = (v, u)
			if self.head[u] != self.head[v]:
				res.append((self.order[self.head[v]], self.order[v] + 1))
				v = self.parent[self.head[v]]
			else:
				res.append((self.order[u] + edge, self.order[v] + 1))
				return res

	def subtree_hld(self, u):
		return (self.order[u], self.order[u] + self.size[u])

	def lca_hld(self, u, v):
		while True:
			if self.order[u] > self.order[v]:
				(u, v) = (v, u)
			if self.head[u] != self.head[v]:
				v = self.parent[self.head[v]]
			else:
				return u

class SegmentTree:

	def __init__(self, arr, func=min, ie=2 ** 63):
		self.h = (len(arr) - 1).bit_length()
		self.n = 2 ** self.h
		self.ie = ie
		self.func = func
		self.tree = [ie for _ in range(2 * self.n)]
		for i in range(len(arr)):
			self.tree[self.n + i] = arr[i]
		for i in range(1, self.n)[::-1]:
			self.tree[i] = func(self.tree[2 * i], self.tree[2 * i + 1])

	def set(self, idx, x):
		idx += self.n
		self.tree[idx] = x
		while idx:
			idx >>= 1
			self.tree[idx] = self.func(self.tree[2 * idx], self.tree[2 * idx + 1])

	def query(self, lt, rt):
		lt += self.n
		rt += self.n
		vl = vr = self.ie
		while rt - lt > 0:
			if lt & 1:
				vl = self.func(vl, self.tree[lt])
				lt += 1
			if rt & 1:
				rt -= 1
				vr = self.func(self.tree[rt], vr)
			lt >>= 1
			rt >>= 1
		return self.func(vl, vr)
import sys
input = sys.stdin.readline
(N, Q) = map(int, input().split())
edge = [tuple(map(int, input().split())) for _ in range(N - 1)]
query = [tuple(map(int, input().split())) for _ in range(Q)]
tree = Tree(N, edge)
tree.setroot(0)
tree.heavylight_decomposition()
arr = [None for _ in range(N)]
for i in range(N):
	arr[tree.order[i]] = (tree.cost[i], 0)

def func(x, y):
	(x0, x1) = x
	(y0, y1) = y
	return (x0 + y0, x1 + y1)
ie = (0, 0)
st = SegmentTree(arr, func, ie)
query_color = [[] for _ in range(N)]
for j in range(Q):
	(x, y, u, v) = query[j]
	query_color[x - 1].append((y, u, v, j))
node_color = [[] for _ in range(N)]
for i in range(1, N):
	node_color[tree.color[i]].append(i)
res = [0 for _ in range(Q)]
for i in range(N):
	for node in node_color[i]:
		st.set(tree.order[node], (0, 1))
	for (y, u, v, j) in query_color[i]:
		for (lt, rt) in tree.range_hld(u - 1, v - 1, True):
			(d, k) = st.query(lt, rt)
			res[j] += d + k * y
	for node in node_color[i]:
		st.set(tree.order[node], (tree.cost[node], 0))
print('\n'.join(map(str, res)))


def f_colorful_tree(N, Q, Edges, Queries):
	import sys
	sys.setrecursionlimit(10 ** 5)
	tree = [[] for _ in range(N)]
	for (a, b, c, d) in Edges:
		tree[a - 1].append((b - 1, c - 1, d))
		tree[b - 1].append((a - 1, c - 1, d))
	depth_from_root = [0] * N
	parent_to_root = [[-1] * N for _ in range(18)]
	distance_from_root = [0] * N

	def dfs1(parent, current, depth, distance):
		depth_from_root[current] = depth
		parent_to_root[0][current] = parent
		distance_from_root[current] = distance
		for (child, _, dist) in tree[current]:
			if child != parent:
				dfs1(current, child, depth + 1, distance + dist)
	dfs1(-1, 0, 0, 0)
	for i in range(17):
		for j in range(N):
			parent_to_root[i + 1][j] = parent_to_root[i][parent_to_root[i][j]]

	def least_common_ancestor(a, b):
		if depth_from_root[a] > depth_from_root[b]:
			(a, b) = (b, a)
		for i in range(18):
			if depth_from_root[b] - depth_from_root[a] & 1 << i:
				b = parent_to_root[i][b]
		if a == b:
			return a
		for i in range(18)[::-1]:
			if parent_to_root[i][a] != parent_to_root[i][b]:
				a = parent_to_root[i][a]
				b = parent_to_root[i][b]
		return parent_to_root[0][a]
	ans = [0] * Q
	v_need = [[] for i in range(N)]
	for (i, (x, y, u, v)) in enumerate(Queries):
		lca = least_common_ancestor(u - 1, v - 1)
		v_need[u - 1].append((i, x - 1, y, 1))
		v_need[v - 1].append((i, x - 1, y, 1))
		v_need[lca].append((i, x - 1, y, -2))
	color_appear = [0] * N
	color_distance = [0] * N

	def dfs2(parent, current):
		nonlocal color_distance
		for (i, x, y, coefficient) in v_need[current]:
			ans[i] += coefficient * (distance_from_root[current] - color_distance[x] + y * color_appear[x])
		for (child, x, y) in tree[current]:
			if child == parent:
				continue
			color_appear[x] += 1
			color_distance[x] += y
			dfs2(current, child)
			color_appear[x] -= 1
			color_distance[x] -= y
	dfs2(-1, 0)
	return '\n'.join(map(str, ans))
(N, Q) = [int(i) for i in input().split()]
Edges = [[int(i) for i in input().split()] for j in range(N - 1)]
Queries = [[int(i) for i in input().split()] for j in range(Q)]
print(f_colorful_tree(N, Q, Edges, Queries))


import sys
read = sys.stdin.buffer.read
readline = sys.stdin.buffer.readline
readlines = sys.stdin.buffer.readlines
(n, q) = map(int, readline().split())
data = list(map(int, read().split()))
abcd = data[:4 * (n - 1)]
xyuv = data[4 * (n - 1):]
root = 1
dbl_max = 20
dbl = [[0] * dbl_max for _ in range(n + 1)]
depth = [0] * (n + 1)
links = [[] for _ in range(n + 1)]
it = iter(abcd)
for (a, b, c, d) in zip(it, it, it, it):
	links[a].append((b, c, d))
	links[b].append((a, c, d))
stack = [root]
while stack:
	i = stack.pop()
	for (j, c, d) in links[i]:
		if j == dbl[i][0]:
			continue
		dbl[j][0] = i
		depth[j] = depth[i] + 1
		stack.append(j)
for i in range(1, dbl_max):
	for j in range(1, n + 1):
		dbl[j][i] = dbl[dbl[j][i - 1]][i - 1]

def get_lca(x, y):
	if depth[x] > depth[y]:
		(x, y) = (y, x)
	dif = depth[y] - depth[x]
	for i in range(dbl_max):
		if dif == 0:
			break
		if dif >> i & 1:
			y = dbl[y][i]
			dif -= 1 << i
	for i in range(dbl_max - 1, -1, -1):
		if dbl[x][i] != dbl[y][i]:
			x = dbl[x][i]
			y = dbl[y][i]
	if x != y:
		return dbl[x][0]
	else:
		return x
ans = [0] * q
query = [[] for _ in range(n + 1)]
for i in range(q):
	(x, y, u, v) = xyuv[i * 4:i * 4 + 4]
	lca = get_lca(u, v)
	query[u].append((i, x, y, 1))
	query[v].append((i, x, y, 1))
	query[lca].append((i, x, y, -2))
tot = 0
c_cost = [[0, 0] for _ in range(n)]
stack = []
for (j, c, d) in links[root]:
	stack.append((root, j, c, d, 1))
while stack:
	(a, b, c, d, num) = stack.pop()
	tot += d * num
	c_cost[c][0] += d * num
	c_cost[c][1] += num
	if num == 1:
		for (qi, x, y, num_q) in query[b]:
			ans[qi] += num_q * (tot - c_cost[x][0] + c_cost[x][1] * y)
		next = []
		for (j, c, d) in links[b]:
			if j == dbl[b][0]:
				stack.append((b, j, c, d, -1))
			else:
				next.append((b, j, c, d, 1))
		stack += next
print('\n'.join(map(str, ans)))


class Accumulate:

	def __init__(self, lst):
		from itertools import accumulate
		self.acc = list(accumulate([0] + lst))

	def query(self, u, v):
		return self.acc[v + 1] - self.acc[u]

class Euler_tour_edge:

	def __init__(self, g, root):

		def dfs(root):
			n = len(g)
			parent = self.parent[0]
			q = [root]
			cnt = 0
			q_wt = [0]
			q_col = [0]
			data_wt = []
			data_col = []
			while q:
				v = q.pop()
				if v >= 0:
					q.append(-v - 1)
					self.ds[v] = cnt
					cnt += 1
					if v != root:
						data_wt.append(q_wt[-1])
						data_col.append(q_col[-1])
					for (c, col, wt) in g[v]:
						if c != parent[v]:
							parent[c] = v
							self.depth[c] = self.depth[v] + 1
							q.append(c)
							q_wt.append(wt)
							q_col.append(col)
				else:
					data_wt.append(-q_wt.pop())
					data_col.append(q_col.pop())
					self.us[-v - 1] = cnt
					cnt += 1
			data_wt.pop()
			data_col.pop()
			return (data_wt, data_col)

		def doubling_make_table(N, logN, Table):
			for i in range(1, logN):
				for (j, Tiij) in enumerate(Table[i - 1]):
					if Tiij != -1:
						Table[i][j] = Table[i - 1][Tiij]
		N = len(g)
		self.logN = len(bin(N)) - 2
		self.parent = [[-1] * N for _ in range(self.logN)]
		self.depth = [0] * N
		self.ds = [0] * n
		self.us = [0] * n
		(data_wt, data_col) = dfs(root)
		doubling_make_table(N, self.logN, self.parent)
		self.wt_acc = Accumulate(data_wt)
		self.color_id = [[] for _ in range(n)]
		self.color_num = [[0] for _ in range(n)]
		self.color_acc = [[0] for _ in range(n)]
		for (i, (c, w)) in enumerate(zip(data_col, data_wt)):
			self.color_id[c].append(i)
			self.color_num[c].append(self.color_num[c][-1] + (1 if w > 0 else -1))
			self.color_acc[c].append(self.color_acc[c][-1] + w)

	def get_path_wt(self, col, wt, u, v):
		p = self.getLCA(u, v)
		res = self.wt_acc.query(self.ds[p], self.ds[u] - 1) + self.wt_acc.query(self.ds[p], self.ds[v] - 1)
		cid = self.color_id[col]
		cnum = self.color_num[col]
		cacc = self.color_acc[col]
		if cid:
			i = bisect_left(cid, self.ds[u])
			res += -cacc[i] + wt * cnum[i]
			i = bisect_left(cid, self.ds[v])
			res += -cacc[i] + wt * cnum[i]
			i = bisect_left(cid, self.ds[p])
			res -= 2 * (-cacc[i] + wt * cnum[i])
		return res

	def getLCA(self, u, v):
		if self.depth[u] > self.depth[v]:
			(u, v) = (v, u)
		dd = self.depth[v] - self.depth[u]
		for k in range(self.logN - 1, -1, -1):
			if dd >> k & 1:
				v = self.parent[k][v]
		if u == v:
			return u
		for k in range(self.logN - 1, -1, -1):
			if self.parent[k][u] != self.parent[k][v]:
				(u, v) = (self.parent[k][u], self.parent[k][v])
		return self.parent[0][u]

	def getdepth(self, u):
		return self.depth[u]
import sys
read = sys.stdin.read
readline = sys.stdin.readline
readlines = sys.stdin.readlines
(n, m) = [int(i) for i in readline().split()]
abcd = map(int, read().split())
g = [[] for _ in range(n)]
for i in range(n - 1):
	(a, b, c, d) = (next(abcd), next(abcd), next(abcd), next(abcd))
	a -= 1
	b -= 1
	g[a].append([b, c, d])
	g[b].append([a, c, d])
E = Euler_tour_edge(g, 0)
ans = [0] * m
from bisect import bisect_left
for i in range(m):
	(c, w, u, v) = (next(abcd), next(abcd), next(abcd), next(abcd))
	u -= 1
	v -= 1
	ans[i] = E.get_path_wt(c, w, u, v)
print('\n'.join(map(str, ans)))


import sys
from collections import defaultdict
readline = sys.stdin.readline
(N, Q) = map(int, readline().split())

def process():
	INF = (N, None)
	M = 2 * N
	M0 = 2 ** (M - 1).bit_length()
	data = [INF] * (2 * M0)
	G = [[] for _ in range(N)]
	dp = {}
	for v in range(N - 1):
		(a, b, c, d) = map(int, readline().split())
		(a, b) = (a - 1, b - 1)
		G[a].append(b)
		G[b].append(a)
		dp[min(a, b), max(a, b)] = (c, d)
	S = [0] * (2 * N)
	F = [0] * (2 * N)
	stack = [0] + G[0][::-1]
	visited = set([0])
	depth = [0] * N
	depthh = [0] * N
	mmn = [[0] for _ in range(N)]
	msn = [[0] for _ in range(N)]
	iin = [[] for _ in range(N)]
	ccs = [0] * N
	path = [0]
	ii = 0
	d = 0
	dd = 0
	while True:
		if len(stack) == 0:
			break
		v = stack.pop()
		if v in visited:
			continue
		ii += 1
		if v >= 0:
			visited.add(v)
			(cc, dpd) = dp[min(v, path[-1]), max(v, path[-1])]
			F[v] = ii
			S[ii] = v
			d += 1
			dd += dpd
			depth[v] = d
			depthh[v] = dd
			ccs[v] = cc
			mmn[cc].append(mmn[cc][-1] + 1)
			msn[cc].append(msn[cc][-1] + dpd)
			iin[cc].append(v)
			path.append(v)
			stack += [-v] + G[v][::-1]
		else:
			(cc, dpd) = dp[min(path[-1], path[-2]), max(path[-1], path[-2])]
			dd -= dpd
			mmn[cc].append(mmn[cc][-1] - 1)
			iin[cc].append(-path[-1])
			msn[cc].append(msn[cc][-1] - dpd)
			F[-path[-1]] = ii
			path.pop()
			S[ii] = path[-1]
			d -= 1
	for (i, v) in enumerate(S):
		data[M0 - 1 + i] = (depth[v], i)
	for i in range(M0 - 2, -1, -1):
		data[i] = min(data[2 * i + 1], data[2 * i + 2])
	for q in range(Q):
		(x, y, u, v) = map(int, readline().split())
		(u, v) = (u - 1, v - 1)
		fu = F[u]
		fv = F[v]
		if fu > fv:
			(fu, fv) = (fv, fu)
		minx = INF
		(a, b) = (fu, fv + 1)
		a += M0
		b += M0
		while a < b:
			if b & 1:
				b -= 1
				minx = min(minx, data[b - 1])
			if a & 1:
				minx = min(minx, data[a - 1])
				a += 1
			a >>= 1
			b >>= 1
		cc = S[minx[1]]
		if len(mmn[x]) - 1 == 0:
			diff = 0
		else:
			(ll, mm, ms, lncc) = (iin[x], mmn[x], msn[x], len(mmn[x]) - 2)
			uvcs = []
			for target_i in [F[u], F[v], F[cc]]:
				(ind, lw, hw) = ((len(mmn[x]) - 2) // 2, 0, len(mmn[x]) - 2)
				if target_i < F[ll[0]] or target_i >= F[ll[lncc]]:
					uvcs.append((0, 0))
				else:
					while True:
						if F[ll[ind]] <= target_i:
							if target_i < F[ll[ind + 1]]:
								uvcs.append((mm[ind + 1], ms[ind + 1]))
								break
							(ind, lw) = ((ind + 1 + hw) // 2, ind + 1)
							continue
						(ind, hw) = ((ind - 1 + lw) // 2, ind - 1)
			diff = (uvcs[0][0] + uvcs[1][0] - 2 * uvcs[2][0]) * y - (uvcs[0][1] + uvcs[1][1] - 2 * uvcs[2][1])
		print(depthh[u] + depthh[v] - 2 * depthh[cc] + diff)
process()


class Doubling:

	def __init__(self, graph, Depth, prev, rootNode=0):
		self.graph = graph
		self.N = len(graph)
		self.Log_N = (self.N - 1).bit_length()
		self.rootNode = rootNode
		self.prev = prev
		self.Depth = Depth
		self.kprev = [self.prev]
		S = self.prev
		for k in range(self.Log_N):
			T = [None] * self.N
			for i in range(self.N):
				if S[i] is None:
					continue
				T[i] = S[S[i]]
			self.kprev.append(T)
			S = T

	def LCAquery(self, u, v):
		dd = self.Depth[v] - self.Depth[u]
		if dd < 0:
			(u, v) = (v, u)
			dd = -dd
		for k in range(self.Log_N + 1):
			if dd & 1:
				v = self.kprev[k][v]
			dd >>= 1
		if u == v:
			return u
		for k in reversed(range(self.Log_N)):
			pu = self.kprev[k][u]
			pv = self.kprev[k][v]
			if pu != pv:
				(u, v) = (pu, pv)
		return self.kprev[0][u]
import sys
input = sys.stdin.readline
sys.setrecursionlimit(10 ** 6)
from bisect import bisect_right
(N, Q) = map(int, input().split())
graph = [[] for _ in range(N)]
for _ in range(N - 1):
	(a, b, c, d) = map(int, input().split())
	graph[a - 1].append((b - 1, c, d))
	graph[b - 1].append((a - 1, c, d))
Query = [list(map(int, input().split())) for _ in range(Q)]
D = [-1] * N
D[0] = 0
EulerTour = []
Color = [0] * N
Weight = [0] * N
PointFront = [-1] * N
prev = [None] * N
Depth = [0] * N

def dfs(p):
	PointFront[p] = len(EulerTour)
	EulerTour.append((p, +1))
	for (np, c, d) in graph[p]:
		if D[np] == -1:
			Color[np] = c
			Weight[np] = d
			D[np] = D[p] + d
			Depth[np] = Depth[p] + 1
			prev[np] = p
			dfs(np)
	EulerTour.append((p, -1))

def dfs2():
	stack = [0]
	while stack:
		p = stack[-1]
		if PointFront[p] == -1:
			PointFront[p] = len(EulerTour)
			EulerTour.append((p, +1))
		for (np, c, d) in graph[p]:
			if D[np] == -1:
				Color[np] = c
				Weight[np] = d
				D[np] = D[p] + d
				Depth[np] = Depth[p] + 1
				prev[np] = p
				stack.append(np)
		if p == stack[-1]:
			EulerTour.append((p, -1))
			stack.pop()
dfs2()
Indexes = [[] for _ in range(N)]
Weights = [[0] for _ in range(N)]
Colors = [[0] for _ in range(N)]
for (i, (p, num)) in enumerate(EulerTour):
	if p == 0:
		continue
	w = Weight[p] * num
	c = Color[p]
	Weights[c].append(Weights[c][-1] + w)
	Colors[c].append(Colors[c][-1] + num)
	Indexes[c].append(i)

def fixed_score(point, color, replace):
	ind = bisect_right(Indexes[color], PointFront[point])
	return D[point] - Weights[color][ind] + Colors[color][ind] * replace

def main():
	doubling = Doubling(graph, Depth, prev)
	ans = []
	for (x, y, u, v) in Query:
		u -= 1
		v -= 1
		lca = doubling.LCAquery(u, v)
		ans.append(fixed_score(u, x, y) + fixed_score(v, x, y) - 2 * fixed_score(lca, x, y))
	print(*ans, sep='\n')
main()


def examA():
	N = DI() / dec(7)
	ans = N
	print(N)
	return

def examB():
	ans = 0
	print(ans)
	return

def examC():
	ans = 0
	print(ans)
	return

def examD():
	ans = 0
	print(ans)
	return

def examE():
	ans = 0
	print(ans)
	return

def examF():

	class LCA(object):

		def __init__(self, G, root=0):
			self.G = G
			self.root = root
			self.n = len(G)
			self.logn = (self.n - 1).bit_length()
			self.depth = [-1 if i != root else 0 for i in range(self.n)]
			self.parent = [[-1] * self.n for _ in range(self.logn)]
			self.W = [-1 if i != root else 0 for i in range(self.n)]
			self.dfs()
			self.doubling()

		def dfs(self):
			que = [self.root]
			while que:
				u = que.pop()
				for (v, (d, c)) in self.G[u].items():
					if self.depth[v] == -1:
						self.depth[v] = self.depth[u] + 1
						self.parent[0][v] = u
						self.W[v] = self.W[u] + d
						que += [v]

		def doubling(self):
			for i in range(1, self.logn):
				for v in range(self.n):
					if self.parent[i - 1][v] != -1:
						self.parent[i][v] = self.parent[i - 1][self.parent[i - 1][v]]

		def get(self, u, v):
			if self.depth[v] < self.depth[u]:
				(u, v) = (v, u)
			du = self.depth[u]
			dv = self.depth[v]
			for i in range(self.logn):
				if dv - du >> i & 1:
					v = self.parent[i][v]
			if u == v:
				return u
			for i in range(self.logn - 1, -1, -1):
				(pu, pv) = (self.parent[i][u], self.parent[i][v])
				if pu != pv:
					(u, v) = (pu, pv)
			return self.parent[0][u]

	def euler_tour(G, root=0):
		n = len(G)
		euler = []
		dq = deque([root])
		dq2 = deque()
		visited = [0] * n
		while dq:
			u = dq.pop()
			euler += [u]
			if visited[u]:
				continue
			for v in G[u].keys():
				if visited[v]:
					dq += [v]
				else:
					dq2 += [v]
			dq.extend(dq2)
			dq2 = deque()
			visited[u] = 1
		return euler
	(N, q) = LI()
	V = [{} for _ in range(N)]
	for _ in range(N - 1):
		(a, b, c, d) = LI()
		V[a - 1][b - 1] = (d, c)
		V[b - 1][a - 1] = (d, c)
	lca = LCA(V)
	colors = [set() for _ in range(N)]
	Q = [LI() for _ in range(q)]
	for (x, y, u, v) in Q:
		u -= 1
		v -= 1
		a = lca.get(u, v)
		colors[u].add(x)
		colors[v].add(x)
		colors[a].add(x)
	ET = euler_tour(V)
	Q_color = [{} for _ in range(N)]
	Q_length = [{} for _ in range(N)]
	for color in colors[0]:
		Q_color[0][color] = 0
		Q_length[0][color] = 0
	S_color = defaultdict(int)
	S_length = defaultdict(int)
	visited = [False] * N
	visited[0] = True
	for i in range(len(ET) - 1):
		(now, ne) = (ET[i], ET[i + 1])
		(d, c) = V[now][ne]
		if visited[ne]:
			S_color[c] -= 1
			S_length[c] -= d
			continue
		else:
			S_color[c] += 1
			S_length[c] += d
		for color in colors[ne]:
			Q_color[ne][color] = S_color[color]
			Q_length[ne][color] = S_length[color]
		visited[ne] = True
	ans = [0] * q
	for (i, (x, y, u, v)) in enumerate(Q):
		u -= 1
		v -= 1
		a = lca.get(u, v)
		child_1 = lca.W[u] + (Q_color[u][x] * y - Q_length[u][x])
		child_2 = lca.W[v] + (Q_color[v][x] * y - Q_length[v][x])
		parent = lca.W[a] + (Q_color[a][x] * y - Q_length[a][x])
		ans[i] = child_1 + child_2 - parent * 2
	for v in ans:
		print(v)
	return
from decimal import getcontext, Decimal as dec
import sys, bisect, itertools, heapq, math, random
from copy import deepcopy
from heapq import heappop, heappush, heapify
from collections import Counter, defaultdict, deque
read = sys.stdin.buffer.read
readline = sys.stdin.buffer.readline
readlines = sys.stdin.buffer.readlines

def I():
	return int(input())

def LI():
	return list(map(int, sys.stdin.readline().split()))

def DI():
	return dec(input())

def LDI():
	return list(map(dec, sys.stdin.readline().split()))

def LSI():
	return list(map(str, sys.stdin.readline().split()))

def LS():
	return sys.stdin.readline().split()

def SI():
	return sys.stdin.readline().strip()
global mod, mod2, inf, alphabet, _ep
mod = 10 ** 9 + 7
mod2 = 998244353
inf = 10 ** 18
_ep = dec('0.000000000001')
alphabet = [chr(ord('a') + i) for i in range(26)]
alphabet_convert = {chr(ord('a') + i): i for i in range(26)}
getcontext().prec = 28
sys.setrecursionlimit(10 ** 7)
examF()


import sys
input = sys.stdin.readline
sys.setrecursionlimit(10 ** 7)
(N, Q) = map(int, input().split())
edge = [[] for i in range(N)]
for i in range(N - 1):
	(a, b, c, d) = map(int, input().split())
	edge[a - 1].append((b - 1, c, d))
	edge[b - 1].append((a - 1, c, d))
prv = [-1] * N
depth = [0] * N
dist = [0 for i in range(N)]

def dfs(v, pv):
	prv[v] = pv
	for (nv, color, weight) in edge[v]:
		if nv != pv:
			depth[nv] = depth[v] + 1
			dist[nv] = dist[v] + weight
			dfs(nv, v)
dfs(0, -1)
LV = (N - 1).bit_length()

def construct(prv):
	kprv = [prv]
	S = prv
	for k in range(LV):
		T = [0] * N
		for i in range(N):
			if S[i] is None:
				continue
			T[i] = S[S[i]]
		kprv.append(T)
		S = T
	return kprv
kprv = construct(prv)

def lca(u, v):
	dd = depth[v] - depth[u]
	if dd < 0:
		(u, v) = (v, u)
		dd = -dd
	for k in range(LV + 1):
		if dd & 1:
			v = kprv[k][v]
		dd >>= 1
	if u == v:
		return u
	for k in range(LV - 1, -1, -1):
		pu = kprv[k][u]
		pv = kprv[k][v]
		if pu != pv:
			u = pu
			v = pv
	return kprv[0][u]
query = []
q = [[] for i in range(N)]
data = {}
for i in range(Q):
	(x, y, u, v) = map(int, input().split())
	LCA = lca(u - 1, v - 1)
	query.append((x, y, u - 1, v - 1, LCA))
	q[u - 1].append(x)
	q[v - 1].append(x)
	q[LCA].append(x)
num = [0 for i in range(N)]
cdist = [0 for i in range(N)]

def dfsc(v, pv, color, weight):
	pn = num[color]
	num[color] += 1
	pd = cdist[color]
	cdist[color] += weight
	for x in q[v]:
		data[v, x] = (0, 0)
		data[v, x] = (num[x], cdist[x])
	for (nv, ncolor, nweight) in edge[v]:
		if nv != pv:
			dfsc(nv, v, ncolor, nweight)
	num[color] = pn
	cdist[color] = pd
dfsc(0, 0, 0, 0)
for (x, y, u, v, LCA) in query:
	d1 = dist[LCA] - data[LCA, x][1] + data[LCA, x][0] * y
	d2 = dist[u] - data[u, x][1] + data[u, x][0] * y
	d3 = dist[v] - data[v, x][1] + data[v, x][0] * y
	print(d2 - d1 + d3 - d1)