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: interview

Starter code:

Solutions:
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))

# doublingに必要なKを求める
for K in range(18) :
    if 2 ** K >= N :
        break

# dfs
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

# doubling        
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]]

# lca
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]

# Queryの先読み
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 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#, gcd
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
        #ans[i] = gr.dist(a, b)
        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')


def __starting_point():
    main()

__starting_point()

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))

# sparse table
# 各インデックスから、2^i 項目分見て、一番depthの小さいインデックス
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
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#, gcd
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 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 init(self, root: int = 0) -> None:
        self.root = root
        self._dfs(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
        #ans[i] = gr.dist(a, b)
        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)

    for an in ans:
        print(an)


def __starting_point():
    main()

__starting_point()

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))))


def f_colorful_tree(N, Q, Edges, Queries):
    # 参考: https://atcoder.jp/contests/abc133/submissions/6316420
    # https://atcoder.jp/contests/abc133/submissions/6321437

    import sys
    sys.setrecursionlimit(10**5)

    # 入力は1-basedだが、頂点と色の番号については0-basedで考える(以降も同様)
    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))

    """Least Common Ancestorを求めるための前処理"""
    depth_from_root = [0] * N
    # [k][v]: 頂点vから2**k個根に近づいたときの頂点番号(根の親は-1とする)
    # 頂点数Nが最大 10**5 なので、2**k >= 10**5 となる k=17 までリストを確保する
    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):
        # editorialと同じことをやる
        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):
        # 色xの辺の距離を一律yに変更したとき、頂点u-v間の距離を求める
        lca = least_common_ancestor(u - 1, v - 1)
        # 色の情報を必要としているのは u, v, lca の3点
        # それぞれの点に対して以下の情報を格納する
        # (何番目のクエリか, どの色の距離を変えるか, 変える距離はいくらか, 係数)
        # 係数は u-v間の距離 = 根-u + 根-v - 2 * 根-LCA(u, v) であることを反映
        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  # [c]: 今訪れている頂点までに色cは何回現れたか
    color_distance = [0] * N  # [c]: 今訪れている頂点まででの色cの辺の距離の和

    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)
            # 例えば a→b→c→b→d と移動したことを a→b→d とみなす
            # (オイラーツアーのようにする)ため、戻ってきたなら引く
            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
input = sys.stdin.readline
sys.setrecursionlimit(10**7)
from collections import defaultdict

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 set_ancestor(x=1, d=0, parent=0):
    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
        set_ancestor(y, d+1, x)

depth = [0] * (N+1)
U = 17
ancestor = [[0] * (U+1) for _ in range(N+1)] # 2**i だけ遡った頂点の番号、行き過ぎは0
set_ancestor()

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, sums = defaultdict(int), nums = defaultdict(int), 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(1)

print('\n'.join(map(str,answer)))

import sys
input = sys.stdin.readline

N,Q=list(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)
    
# LCA(オイラーツアー＋Segment tree)

DEPTH=[-1]*(N+1)
DEPTH[1]=0

from collections import deque
QUE = deque([1])
QUE2 = deque()
EULER=[]#(i,j)で,(1からツアーで辿った点の深さ,そのindex)

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]=ind

LEN=len(EULER)

seg_el=1<<(LEN.bit_length())#Segment treeの台の要素数
SEG=[(1<<30,0)]*(2*seg_el)#1-indexedなので、要素数2*seg_el.Segment treeの初期値で初期化

for i in range(LEN):#Dを対応する箇所へupdate
    SEG[i+seg_el]=EULER[i]

for i in range(seg_el-1,0,-1):#親の部分もupdate
    SEG[i]=min(SEG[i*2],SEG[i*2+1])

def update(n,x,seg_el):#A[n]をxへ更新（反映）
    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,r)に関するminを調べる
    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)

N_q=[[] for i in range(N+1)]
for ind,(c,pl,x,y) in enumerate(Query):
    N_q[x].append([ind,c,pl,1])
    N_q[y].append([ind,c,pl,1])
    N_q[LCA(x,y)[1]].append([ind,c,pl,0])

ANS=[0]*Q

LENG=0
C_LENGTH=[0]*N
C_SUM=[0]*N

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

        for que,c,pl,boo in N_q[ind]:
            if boo==1:
                ANS[que]+=LENG+C_SUM[c]*pl-C_LENGTH[c]

            else:
                ANS[que]-=(LENG+C_SUM[c]*pl-C_LENGTH[c])*2
            
    else:
        LENG-=l
        C_LENGTH[c]-=l
        C_SUM[c]-=1

for ans in ANS:
    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#, gcd
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 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([x-1 for x in lst])
def INC(lst: list): return list([x+1 for x in lst])

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 init(self, root: int = 0) -> None:
        self.root = root
        self._dfs(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 __call__(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
        for i in range(L_1, -1, -1):
            leng = 1 << i
            if gap >= leng:
                gap -= leng
                b = self.par[b][i]
        if a == b:  
            return a
        for i in range(L_1, -1, -1):
            na = self.par[a][i]  
            nb = self.par[b][i]  
            if na != nb:
                a, b = na, nb
        return self.par[a][0]
    def length(self, a: int, b: int) -> int:
        c = self.__call__(a, b)
        return self.dep[a] + self.dep[b] - self.dep[c] * 2
    def dist(self, a: int, b: int):
        c = self.__call__(a, b)
        return self.costs[a] + self.costs[b] - self.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(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)
    for i in range(Q):
        print((ans[i]))


def __starting_point():
    main()

__starting_point()

import sys
sys.setrecursionlimit(10**7)

N, Q = list(map(int, input().split()))
edge = [[] for _ in range(N+1)]
for _ in range(N-1):
        a, b, c, d = list(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 = list(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
input = sys.stdin.readline
sys.setrecursionlimit(10**8)


class SegmentTree1():
    """
    1-indexed Segment Tree
    """
    def __init__(self, n_, ele_id, operation_func):
        self.n = 1 << (n_ - 1).bit_length()  # size
        self.data = [ele_id] * (2 * self.n)  # binary tree
        self.ele_id = ele_id  # identity element
        self.operation_func = operation_func  # binary operation of monoid

    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):
        # change idx-th element to x (idx : 0-indexed)
        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):
        # query for interval [l, r) (l, r : 0-indexed)
        l += self.n
        r += self.n
        ret = self.ele_id
        while l < r:
            if l & 1:  # right child
                ret = self.operation_func(ret, self.data[l])
                l += 1
            if r & 1:  # right child
                r -= 1
                ret = self.operation_func(ret, self.data[r])
            # go to parent-nodes
            l = l >> 1
            r = r >> 1
        return ret


class Tree():
    def __init__(self, n, graph, v_root):
        self.n = n  # number of nodes
        self.graph = graph  # adjacent list of graph
        self.v_root = v_root  # root node

        # euler tour
        self.first_idx = [2 * self.n] * self.n
        self.euler_tour = []
        self.euler_depth_topo = []
        self.euler_depth_real = []
        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 = list(map(int, input().split()))
graph = [[] for _ in range(n)]
for _ in range(n - 1):
    a, b, c, d = list(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 = list(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)


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 = list(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 = list(map(int, input().split()))
f(n,q)


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()

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)
 
    # Initialize data
    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])[:]
    
    # Update ak as x
    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])[:]
    
    # Min value in [l, r)
    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)
    
    # Lowest ancestor of u, v
    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:
    # Calc basic distance
    val = memo[u][0] + memo[v][0] - memo[ca][0] * 2
    # Append color diff
    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)))

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

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)] # 2**i だけ遡った頂点の番号、行き過ぎは0

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
sys.setrecursionlimit(10**6+1) #再帰関数の上限,10**5以上の場合python


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())
N, Q = map(int, input().split())
#A = tuple(map(int, input().split())) #1行ベクトル
#L = tuple(int(input()) for i in range(N)) #改行ベクトル
#S = tuple(tuple(map(int, input().split())) for i in range(N)) #改行行列

edge = [[] for i in range(N)]
for i in range(N-1):#木の場合M=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)) #有向のばあいコメントアウト



#ダブリングによるLCA, 前処理NlogN, 呼び出しlogN, 安定  
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()#ここをpopleftにすると幅優先探索BFSになる
        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]]


# LCAの計算
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)

# 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):
#   nonlocal 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")

# https://atcoder.jp/contests/abc133/submissions/6316420
# 参考にさせていただきました。

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))

# ここで前処理
# LCAができるようにしておく
dep=[0]*n
par=[[-1]*n for i in range(18)]
# ついでに距離も持っておく
dis=[0]*n

# parent, now_point, depth, dist

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]]

# LCAの準備が完了

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)
    # 色の情報がほしいのは u, v, lの3点
    # どのクエリに属するか, どの色を変えるか、距離はいくつにするか, 係数
    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)
        # オイラーツアーのように、戻ってきたら引く
        # これによって、a→b→c→b→dと移動した時a→b→dとみなせる
        color[x] -= 1
        cdist[x] -= y
dfs2(0, -1)

for i in ans:
    print(i)

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

def solve():
    INF = float('inf')

    N, Q = list(map(int, input().split()))
    adjL = [[] for _ in range(N)]
    for _ in range(N-1):
        A, B, C, D = list(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  # min
    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(list(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()


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): # vの方が深いようにする
            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)

    # Queryを先読みする
    Qs=[[] for _ in range(n)] #Q[v]: idx, color, mag, coef
    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))

    # dfsでクエリに答えていく
    ans=[0]*q
    S=[[0,0] for _ in range(n)] # S[col]: total num, total len
    def dfs(v,p):
        # クエリに答える
        # 現在のlengthから、色のtotal lenを引き、青の個数*倍率(mug)を足したものが変更後の長さ
        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
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#, gcd
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 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 init(self, root: int = 0) -> None:
        self.root = root
        self._dfs(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 __call__(self, a: int, b: int) -> int:
    def lcs(self, a: int, b: int) -> int:
        dep_s, dep_l = self.dep[a], self.dep[b]
        self_par = self.par
        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
        for i in range(L_1, -1, -1):
            leng = 1 << i
            if gap >= leng:
                gap -= leng
                b = self_par[b][i]
        if a == b:  
            return a
        for i in range(L_1, -1, -1):
            na = self_par[a][i]
            nb = self_par[b][i]
            if na != nb:
                a, b = na, nb
        return self_par[a][0]
    def length(self, a: int, b: int) -> int:
        #c = self.__call__(a, b)
        c = self.lcs(a, b)
        return self.dep[a] + self.dep[b] - self.dep[c] * 2
    def dist(self, a: int, b: int):
        #c = self.__call__(a, b)
        c = self.lcs(a, b)
        return self.costs[a] + self.costs[b] - self.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
        #ans[i] = gr.dist(a, b)
        c = gr.lcs(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)

    for an in ans:
        print(an)


def __starting_point():
    main()

__starting_point()

import sys
sys.setrecursionlimit(10**6+1) #再帰関数の上限,10**5以上の場合python


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())
N, Q = map(int, input().split())
#A = tuple(map(int, input().split())) #1行ベクトル
#L = tuple(int(input()) for i in range(N)) #改行ベクトル
#S = tuple(tuple(map(int, input().split())) for i in range(N)) #改行行列

edge = [[] for i in range(N)]
for i in range(N-1):#木の場合M=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)) #有向のばあいコメントアウト



#ダブリングによるLCA, 前処理NlogN, 呼び出しlogN, 安定  
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()#ここをpopleftにすると幅優先探索BFSになる
        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]]


# LCAの計算
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):
#     ans[i]=dist[u-1]+dist[v-1]-dist[lca(u-1,v-1)]
#     qn[u-1].append((i,x-1,y))
#     qn[v-1].append((i,x-1,y))
# cnt=[0]*Q
# #printe(qn)

# qc=[[0,0] for _ in range(Q)]
# 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 in qn[e]:
#         if cnt[qi]==0:
#             qc[qi][0]-=cc[x][0]
#             qc[qi][1]-=cc[x][1]
#         elif cnt[qi]==1:
#             qc[qi][0]+=cc[x][0]
#             qc[qi][1]+=cc[x][1]
#             ans[qi]+=y*qc[qi][0]-qc[qi][1]
#         cnt[qi]+=1
#     for ne,nc,nd in edge[e]:
#         if ne==par[e]:continue
#         dfs2(ne,nc,nd,e)
#     if c!=None:
#         cc[c][0]+=1
#         cc[c][1]+=d
# dfs2(0)
# printl(ans)
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):
  nonlocal 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")

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):
    # idx, color, mag, coef
        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**6+1) #再帰関数の上限,10**5以上の場合python


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())
N, Q = map(int, input().split())
#A = tuple(map(int, input().split())) #1行ベクトル
#L = tuple(int(input()) for i in range(N)) #改行ベクトル
#S = tuple(tuple(map(int, input().split())) for i in range(N)) #改行行列

edge = [[] for i in range(N)]
for i in range(N-1):#木の場合M=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)) #有向のばあいコメントアウト



#ダブリングによるLCA, 前処理NlogN, 呼び出しlogN, 安定  
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()#ここをpopleftにすると幅優先探索BFSになる
        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]]


# LCAの計算
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):
    ans[i]=dist[u-1]+dist[v-1]-2*dist[lca(u-1,v-1)]
    qn[u-1].append((i,x-1,y,1))
    qn[v-1].append((i,x-1,y,1))
    qn[lca(u-1,v-1)].append((i,x-1,y,-2))
cnt=[0]*Q
#printe(qn)

qc=[[0,0] for _ in range(Q)]
cc=[[0,0] for _ in range(N-1)]

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

# 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):
#   nonlocal 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")

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

    # assert depth[u] <= depth[v]
    for k in range(LV+1):
        if dd & 1:
            v = kprv[k][v]
        dd >>= 1

    # assert depth[u] == depth[v]
    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

    # assert kprv[0][u] == kprv[0][v]
    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)

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


class SegmentTree1():
    """
    1-indexed Segment Tree
    """
    def __init__(self, n_, ele_id, operation_func):
        self.n = 1 << (n_ - 1).bit_length()  # size
        self.data = [ele_id] * (2 * self.n)  # binary tree
        self.ele_id = ele_id  # identity element
        self.operation_func = operation_func  # binary operation of monoid

    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):
        # change idx-th element to x (idx : 0-indexed)
        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):
        # query for interval [l, r) (l, r : 0-indexed)
        l += self.n
        r += self.n
        ret = self.ele_id
        while l < r:
            if l & 1:  # right child
                ret = self.operation_func(ret, self.data[l])
                l += 1
            if r & 1:  # right child
                r -= 1
                ret = self.operation_func(ret, self.data[r])
            # go to parent-nodes
            l = l >> 1
            r = r >> 1
        return ret


class Tree():
    def __init__(self, n, graph, v_root):
        self.n = n  # number of nodes
        self.graph = graph  # adjacent list of graph
        self.v_root = v_root  # root node

        # euler tour
        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 = list(map(int, input().split()))
graph = [[] for _ in range(n)]
for _ in range(n - 1):
    a, b, c, d = list(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 = list(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
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 list(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 の構築
    euler_tour = []
    first_appear = [0] * n
    depth = [0] * n

    dfs(0, -1, 0)
    # print(euler_tour)
    # print(first_appear)
    # print(depth)

    # LCAを計算するクエリの前計算
    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])

    # print(data)

    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 = list(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(10**6+1) #再帰関数の上限,10**5以上の場合python


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())
N, Q = map(int, input().split())
#A = tuple(map(int, input().split())) #1行ベクトル
#L = tuple(int(input()) for i in range(N)) #改行ベクトル
#S = tuple(tuple(map(int, input().split())) for i in range(N)) #改行行列

edge = [[] for i in range(N)]
for i in range(N-1):#木の場合M=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)) #有向のばあいコメントアウト



#ダブリングによるLCA, 前処理NlogN, 呼び出しlogN, 安定  
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()#ここをpopleftにすると幅優先探索BFSになる
        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]]


# LCAの計算
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):
    ans[i]=dist[u-1]+dist[v-1]-2*dist[lca(u-1,v-1)]
    qn[u-1].append((i,x-1,y,1))
    qn[v-1].append((i,x-1,y,1))
    qn[lca(u-1,v-1)].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)

# 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):
#   nonlocal 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")

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):
  nonlocal 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
sys.setrecursionlimit(10**7)
from collections import defaultdict

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)]

def set_ancestor(x=1, d=0, parent=0):
    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
        set_ancestor(y, d+1, x)

depth = [0] * (N+1)
U = 17
ancestor = [[0] * (U+1) for _ in range(N+1)] # 2**i だけ遡った頂点の番号、行き過ぎは0
set_ancestor()

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
sys.setrecursionlimit(10**7)

def main0(n,g,xyuv):
  # LCAを求める。自作する。
  # 各頂点の深さ。根は深さ0
  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])
    # 区間[l,r)へのクエリ
    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
    #print(uvlen-uvsum+uvnum*y,uvlen,usum,vsum,wsum,uvnum,w)
    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

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
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  # , gcd
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
        # ans[i] = gr.dist(a, b)
        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


    sk = []
    tr = [{} for _ in range(N+1)]
    vis = [False] * (N+1)

    def dfs2(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

        vis[v] = True

        for (to, co, col) in reversed(es[v]):
            if to == p:
                continue
            sk.append((to, v, col, co))
            tr[v][to] = (col, co)

        while len(sk) > 0:
            v, p, cl, cs = sk[-1]
            if len(es[v]) == 1:
                sk.pop()

            if vis[v] and p >= 0:
                cl, cs = tr[p][v]
                cnt[cl] -= 1
                sum_[cl] -= cs
                tr[p].pop(v)
                sk.pop()
                continue

            cnt[cl] += 1
            sum_[cl] += cs

            for (col, qid, coeff, dist) in qs[v]:
                x = -sum_[col]
                x += dist * cnt[col]
                ans[qid] += x * coeff

            vis[v] = True

            for (to, co, col) in reversed(es[v]):
                if to == p:
                    continue
                sk.append((to, v, col, co))
                tr[v][to] = (col, co)

            if len(tr[v]) == 0 and p >= 0:
                cl, cs = tr[p][v]
                cnt[cl] -= 1
                sum_[cl] -= cs
                tr[p].pop(v)

    dfs2(0)

    print(*ans, sep='\n')


def __starting_point():
    main()

__starting_point()