Problem:


Score : 600 points

Problem Statement

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 1

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 1

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.



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

Submission status: Accepted

Solution code:
import sys
sys.setrecursionlimit(10**5+5)
from collections import defaultdict,deque
input = sys.stdin.readline
class LCA:

    def __init__(self,n):
        self.size = n+1
        self.bitlen = n.bit_length()
        self.lca = [[0]*self.size for i in range(self.bitlen)]
        self.depth = [-1]*self.size
        self.dis = [-1]*self.size
        self.depth[1] = 0
        self.dis[1] = 0
    def make(self,root):
        q = deque([root])
        while q:
            now = q.popleft()
            for nex,c,w in e[now]:
                if self.depth[nex]>= 0:
                    continue
                self.depth[nex] = self.depth[now]+1
                self.dis[nex] = self.dis[now]+w
                self.lca[0][nex] = now
                q.append(nex)
        for i in range(1,self.bitlen):
            for j in range(self.size):
                if self.lca[i-1][j] > 0:
                    self.lca[i][j] = self.lca[i-1][self.lca[i-1][j]]
    
    def search(self,x,y):
        dx = self.depth[x]
        dy = self.depth[y]
        if dx < dy:
            x,y = y,x
            dx,dy = dy,dx
        dif = dx-dy
        while dif:
            s = dif & (-dif)
            x = self.lca[s.bit_length()-1][x]
            dif -= s
        if x == y:
            return x
        for i in range(self.bitlen-1,-1,-1):
            if self.lca[i][x] != self.lca[i][y]:
                x = self.lca[i][x]
                y = self.lca[i][y]
        
        return self.lca[0][x]

n,q = map(int,input().split())
e = [[] for i in range(n+1)]
for i in range(n-1):
    a,b,c,d = map(int,input().split())
    e[a].append((b,c,d))
    e[b].append((a,c,d))
    
lca = LCA(n)
lca.make(1)

Q = [[] for i in range(n+1)]
ans = []
for i in range(q):
    x,y,u,v = map(int,input().split())
    a = lca.search(u,v)
    ans.append(lca.dis[u]+lca.dis[v]-2*lca.dis[a])
    Q[u].append((x,y,i,1))
    Q[v].append((x,y,i,1))
    Q[a].append((x,y,i,-2))


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

for i in ans:
    print(i)

Submission status: Accepted

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

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

def dfs1(now,p,count1,count2):
    for cou in dcount[now]:
        dcount[now][cou] = count1[cou]
    for wei in dweight[now]:
        dweight[now][wei] = count2[wei]

    for nex,c,w in e[now]:
        if nex == p:
            continue
        count1[c] += 1
        count2[c] += w
        dfs1(nex,now,count1,count2)
        count1[c] -= 1
        count2[c] -= w
n,q = map(int,input().split())
e = [[] for i in range(n+1)]

for i in range(n-1):
    a,b,c,d = map(int,input().split())
    e[a].append((b,c,d))
    e[b].append((a,c,d))
size = n+1
bitlen = 19
lca = [[0]*size for i in range(bitlen)]
depth = [-1]*size
dis = [-1]*size
depth[1] = 0
dis[1] = 0

bfs()
for i in range(1,bitlen):
    for j in range(1,size):
        if lca[i-1][j] > 0:
            lca[i][j] = lca[i-1][lca[i-1][j]]
    
def search(x,y):
    dx = depth[x]
    dy = depth[y]
    if dx < dy:
        x,y = y,x
        dx,dy = dy,dx
    dif = dx-dy
    while dif:
        s = dif & (-dif)
        x = lca[s.bit_length()-1][x]
        dif -= s
    if x == y:
        return x
    for i in range(bitlen-1,-1,-1):
        if lca[i][x] != lca[i][y]:
            x = lca[i][x]
            y = lca[i][y]
    return lca[0][x]


Q = [[] for i in range(n+1)]
ans = []
for i in range(q):
    x,y,u,v = map(int,input().split())
    a = search(u,v)
    ans.append(dis[u]+dis[v]-2*dis[a])
    Q[u].append((x,y,i,1))
    Q[v].append((x,y,i,1))
    Q[a].append((x,y,i,-2))


count = defaultdict(int)
weight = defaultdict(int)

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

for i in ans:
    print(i)

Submission status: Accepted

Solution code:
import sys
sys.setrecursionlimit(10**5+5)
from collections import defaultdict,deque
input = sys.stdin.readline
class LCA:

    def __init__(self,n):
        self.size = n+1
        self.bitlen = n.bit_length()
        self.lca = [[0]*self.size for i in range(self.bitlen)]
        self.depth = [-1]*self.size
        self.dis = [-1]*self.size
        self.depth[1] = 0
        self.dis[1] = 0
    def make(self,root):
        q = deque([root])
        while q:
            now = q.popleft()
            for nex,c,w in e[now]:
                if self.depth[nex]>= 0:
                    continue
                self.depth[nex] = self.depth[now]+1
                self.dis[nex] = self.dis[now]+w
                self.lca[0][nex] = now
                q.append(nex)
        for i in range(1,self.bitlen):
            for j in range(self.size):
                if self.lca[i-1][j] > 0:
                    self.lca[i][j] = self.lca[i-1][self.lca[i-1][j]]
    
    def search(self,x,y):
        dx = self.depth[x]
        dy = self.depth[y]
        if dx < dy:
            x,y = y,x
            dx,dy = dy,dx
        dif = dx-dy
        while dif:
            s = dif & (-dif)
            x = self.lca[s.bit_length()-1][x]
            dif -= s
        if x == y:
            return x
        for i in range(self.bitlen-1,-1,-1):
            if self.lca[i][x] != self.lca[i][y]:
                x = self.lca[i][x]
                y = self.lca[i][y]
        
        return self.lca[0][x]

n,q = map(int,input().split())
e = [[] for i in range(n+1)]
for i in range(n-1):
    a,b,c,d = map(int,input().split())
    e[a].append((b,c,d))
    e[b].append((a,c,d))
    
lca = LCA(n)
lca.make(1)

Q = []
dcount = defaultdict(lambda : defaultdict(int))
dweight = defaultdict(lambda : defaultdict(int))
for i in range(q):
    x,y,u,v = map(int,input().split())
    a = lca.search(u,v)
    dcount[u][x] = 0
    dcount[v][x] = 0
    dcount[a][x] = 0
    dweight[u][x] = 0
    dweight[v][x] = 0
    dweight[a][x] = 0
    Q.append((x,y,u,v,a))

dis = [0]*(n+1)


def dfs1(now,p,count1,count2):
    for cou in dcount[now]:
        dcount[now][cou] = count1[cou]
    for wei in dweight[now]:
        dweight[now][wei] = count2[wei]

    for nex,c,w in e[now]:
        if nex == p:
            continue
        count1[c] += 1
        count2[c] += w
        dis[nex] =dis[now]+w
        dfs1(nex,now,count1,count2)
        count1[c] -= 1
        count2[c] -= w
    
dfs1(1,0,defaultdict(int),defaultdict(int))

for x,y,u,v,a in Q:
    cal = dis[u]+dis[v]-2*dis[a]
    cal += y*(dcount[u][x]+dcount[v][x]-2*dcount[a][x]) - dweight[u][x]-dweight[v][x]+2*dweight[a][x]
    print(cal)

Submission status: Accepted

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

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

def dfs1(now,p,count1,count2):
    for cou in dcount[now]:
        dcount[now][cou] = count1[cou]
    for wei in dweight[now]:
        dweight[now][wei] = count2[wei]

    for nex,c,w in e[now]:
        if nex == p:
            continue
        count1[c] += 1
        count2[c] += w
        dfs1(nex,now,count1,count2)
        count1[c] -= 1
        count2[c] -= w
n,q = map(int,input().split())
e = [[] for i in range(n+1)]

for i in range(n-1):
    a,b,c,d = map(int,input().split())
    e[a].append((b,c,d))
    e[b].append((a,c,d))
size = n+1
bitlen = 19
lca = [[0]*size for i in range(bitlen)]
depth = [-1]*size
dis = [-1]*size
depth[1] = 0
dis[1] = 0

bfs()
for i in range(1,bitlen):
    for j in range(1,size):
        if lca[i-1][j] > 0:
            lca[i][j] = lca[i-1][lca[i-1][j]]
    
def search(x,y):
    dx = depth[x]
    dy = depth[y]
    if dx < dy:
        x,y = y,x
        dx,dy = dy,dx
    dif = dx-dy
    while dif:
        s = dif & (-dif)
        x = lca[s.bit_length()-1][x]
        dif -= s
    if x == y:
        return x
    for i in range(bitlen-1,-1,-1):
        if lca[i][x] != lca[i][y]:
            x = lca[i][x]
            y = lca[i][y]
    return lca[0][x]


Q = []
dcount = defaultdict(lambda : defaultdict(int))
dweight = defaultdict(lambda : defaultdict(int))
for i in range(q):
    x,y,u,v = map(int,input().split())
    a = search(u,v)
    dcount[u][x] = 0
    dcount[v][x] = 0
    dcount[a][x] = 0
    dweight[u][x] = 0
    dweight[v][x] = 0
    dweight[a][x] = 0
    Q.append((x,y,u,v,a))

    
dfs1(1,0,defaultdict(int),defaultdict(int))

for x,y,u,v,a in Q:
    cal = dis[u]+dis[v]-2*dis[a]
    cal += y*(dcount[u][x]+dcount[v][x]-2*dcount[a][x]) - dweight[u][x]-dweight[v][x]+2*dweight[a][x]
    print(cal)

Submission status: Accepted

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

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

def dfs1(now,p,count1,count2):
    for cou in dcount[now]:
        dcount[now][cou] = count1[cou]
    for wei in dweight[now]:
        dweight[now][wei] = count2[wei]

    for nex,c,w in e[now]:
        if nex == p:
            continue
        count1[c] += 1
        count2[c] += w
        dfs1(nex,now,count1,count2)
        count1[c] -= 1
        count2[c] -= w
n,q = map(int,input().split())
e = [[] for i in range(n+1)]

for i in range(n-1):
    a,b,c,d = map(int,input().split())
    e[a].append((b,c,d))
    e[b].append((a,c,d))
size = n+1
bitlen = 19
lca = [[0]*size for i in range(bitlen)]
depth = [-1]*size
dis = [-1]*size
depth[1] = 0
dis[1] = 0

bfs()
for i in range(1,bitlen):
    for j in range(1,size):
        if lca[i-1][j] > 0:
            lca[i][j] = lca[i-1][lca[i-1][j]]
    
def search(x,y):
    if depth[x] > depth[y]:
        x,y = y,x
    for i in range(bitlen):
        if ((depth[y]-depth[x])>>i) & 1:
            y = lca[i][y]
    if x == y:
        return x
    for i in range(bitlen-1,-1,-1):
        if lca[i][x] != lca[i][y]:
            x = lca[i][x]
            y = lca[i][y]
    return lca[0][x]


Q = []
dcount = defaultdict(lambda : defaultdict(int))
dweight = defaultdict(lambda : defaultdict(int))
for i in range(q):
    x,y,u,v = map(int,input().split())
    a = search(u,v)
    dcount[u][x] = 0
    dcount[v][x] = 0
    dcount[a][x] = 0
    dweight[u][x] = 0
    dweight[v][x] = 0
    dweight[a][x] = 0
    Q.append((x,y,u,v,a))

    
dfs1(1,0,defaultdict(int),defaultdict(int))

for x,y,u,v,a in Q:
    cal = dis[u]+dis[v]-2*dis[a]
    cal += y*(dcount[u][x]+dcount[v][x]-2*dcount[a][x]) - dweight[u][x]-dweight[v][x]+2*dweight[a][x]
    print(cal)

Submission status: Accepted

Solution code:
def numba_compile(numba_config):
    import os, sys
    if sys.argv[-1] == "ONLINE_JUDGE":
        from numba import njit
        from numba.pycc import CC
        cc = CC("my_module")
        for func, signature in numba_config:
            globals()[func.__name__] = njit(signature)(func)
            cc.export(func.__name__, signature)(func)
        cc.compile()
        exit()
    elif os.name == "posix":
        exec(f"from my_module import {','.join(func.__name__ for func, _ in numba_config)}")
        for func, _ in numba_config:
            globals()[func.__name__] = vars()[func.__name__]
    else:
        from numba import njit
        for func, signature in numba_config:
            globals()[func.__name__] = njit(signature, cache=True)(func)
        print("compiled!", file=sys.stderr)

class HLD:
    # HL 分解
    # 検証1 (distance) 閉路: https://atcoder.jp/contests/abc014/submissions/13991240
    # 検証2 (lca) [yosupo] Lowest Common Ancestor: https://judge.yosupo.jp/submission/12490
    def __init__(self, E, root=1):
        # E は双方向に辺を張った木で、破壊的に有向グラフに変更される
        # O(N)
        self.E = E
        self.root = root
        self.N = N = len(E)  # 頂点数
        self.Parent = [-1] * N  # 頂点番号 v -> 親ノード
        self.Size = [-1] * N  # 頂点番号 v -> 部分木のサイズ
        self.dfs1()

        self.Mapping = [-1] * N  # 頂点番号 v -> 内部インデックス
        self.Head = list(range(N))  # 頂点番号 v -> v を含む heavy path の左端の頂点番号
        self.Depth = [0] * N  # 頂点番号 v -> 深さ（root から v までの距離）
        self.dfs2()

    def dfs1(self):
        E = self.E
        Parent, Size = self.Parent, self.Size
        Path = [self.root]
        Idx_edge = [0]
        while Path:
            v = Path[-1]
            idx_edge = Idx_edge[-1]
            Ev = E[v]
            if idx_edge != len(Ev):
                # 行きがけ・通りがけ 辺の数だけ実行される
                u = Ev[idx_edge]
                Idx_edge[-1] += 1
                E[u].remove(v)  # 有向グラフならここをコメントアウトする
                Parent[u] = v
                Path.append(u)
                Idx_edge.append(0)
            else:
                # 帰りがけ 頂点の数だけ実行される
                if len(Ev) >= 2:
                    ma = -1
                    argmax = None
                    for i, u in enumerate(Ev):
                        if Size[u] > ma:
                            ma = Size[u]
                            argmax = i
                    u0, um = Ev[0], Ev[argmax]
                    Size[u0], Size[um] = Size[um], Size[u0]
                    Ev[0], Ev[argmax] = Ev[argmax], Ev[0]
                Size[v] = sum(Size[u] for u in Ev) + 1
                Path.pop()
                Idx_edge.pop()

    def dfs2(self):
        E = self.E
        Mapping = self.Mapping
        Head = self.Head
        Depth = self.Depth
        k = 0
        St = [self.root]
        while St:
            v = St.pop()
            Mapping[v] = k
            k += 1
            Ev = E[v]
            if Ev:
                Head[Ev[0]] = Head[v]
                St += Ev[::-1]
                for u in Ev:
                    Depth[u] = Depth[v] + 1  # distance を使わないのならここをコメントアウトする

    def lca(self, v, u):
        # O(logN)
        Parent = self.Parent
        Mapping = self.Mapping
        Head = self.Head
        while True:
            if Mapping[v] > Mapping[u]:
                v, u = u, v  # v の方を根に近くする
            if Head[v] == Head[u]:
                return v
            u = Parent[Head[u]]

    def distance(self, v, u):
        # O(logN)
        Depth = self.Depth
        return Depth[v] + Depth[u] - 2 * Depth[self.lca(v, u)]

    def get_for_each_edge(self, v, u, identity, merge, func):
        Mapping = self.Mapping
        Head = self.Head
        Parent = self.Parent
        res = identity
        while True:
            if Mapping[v] > Mapping[u]:
                v, u = u, v
            if Head[u] != Head[v]:
                res = merge(res, func(Mapping[Head[u]], Mapping[u]+1))
                u = Parent[Head[u]]
            else:
                if v != u:
                    res = merge(res, func(Mapping[v]+1, Mapping[u]+1))
                return res

# >>> binary indexed tree >>>
# 必要な要素数+1 の長さの ndarray の 1 要素目以降を使う
def bitify(arr):  # [bitify, "void(i8[:])"],
    # len(arr) は 2 冪 + 1
    for i in range(1, len(arr)-1):
        arr[i + (i & -i)] += arr[i]
def bit_sum(bit, i):  # [bit_sum, "i8(i8[:],i8)"],
    # (0, i]
    res = 0
    while i:
        res += bit[i]
        i -= i & -i
    return res
def bit_add(bit, i, val):  # [bit_add, "void(i8[:],i8,i8)"],
    n = len(bit)
    while i < n:
        bit[i] += val
        i += i & -i
# <<< binary indexed tree <<<

numba_compile([
    [bitify, "void(i8[:])"],
    [bit_sum, "i8(i8[:],i8)"],
    [bit_add, "void(i8[:],i8,i8)"],
])

import sys
from operator import itemgetter, add
from itertools import groupby
from functools import partial
import numpy as np

def main():
    N, Q = map(int, sys.stdin.buffer.readline().split())
    m = list(map(int, sys.stdin.buffer.read().split()))
    ABCD, XYUV = m[:(N-1)*4], m[(N-1)*4:]
    G = [[] for _ in range(N+1)]
    for a, b in zip(ABCD[::4], ABCD[1::4]):
        G[a].append(b)
        G[b].append(a)
    hld = HLD(G)
    weights = [0] * (1<<N.bit_length())
    colors = [[] for _ in range(N)]
    m = iter(ABCD)
    for a, b, c, d in zip(m, m, m, m):
        v, u = hld.Mapping[a], hld.Mapping[b]
        edge = (v if v > u else u) + 1
        weights[edge] = d
        colors[c].append(edge)
    bit = np.array(weights, dtype=np.int64)
    bitify(bit)
    bit_add_ = partial(bit_add, bit)
    bit_sum_ = partial(bit_sum, bit)
    m = iter(XYUV)
    IXYUV = sorted(zip(range(Q), m, m, m, m), key=itemgetter(1))
    Ans = [0] * Q
    base = 1<<32
    mask = base-1
    for x, group in groupby(IXYUV, key=itemgetter(1)):
        for edge in colors[x]:
            bit_add_(edge, base-weights[edge])
        for i, _, y, u, v in group:
            a = hld.get_for_each_edge(u, v, 0, add, lambda p, q: bit_sum_(q)-bit_sum_(p))
            a = (a>>32) * y + (a&mask)
            Ans[i] = a
        for edge in colors[x]:
            bit_add_(edge, weights[edge]-base)
    print("\n".join(map(str, Ans)))

main()

Submission status: Accepted

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

def bfs():
    q = deque([0])
    dep = [-1]*N
    dist = [-1]*N
    dep[0] = 0
    dist[0] = 0
    
    while q:
        v = q.popleft()
        
        for nv, _, w in G[v]:
            if dep[nv]==-1:
                parent[0][nv] = v
                dep[nv] = dep[v]+1
                dist[nv] = dist[v]+w
                q.append(nv)
    
    return dep, dist
    
def lca(u, v):
    if dep[u]>dep[v]:
        u, v = v, u
    
    for i in range(log_size):
        if ((dep[v]-dep[u])>>i)&1:
            v = parent[i][v]
    
    if u==v:
        return u
    
    for i in range(log_size-1, -1, -1):
        if parent[i][u]!=parent[i][v]:
            u, v = parent[i][u], parent[i][v]
    
    return parent[0][u]
    
def calc_dist(u, v):
    return dist[u]+dist[v]-2*dist[lca(u, v)]

def dfs(v, pv, now_cnt, now_wet):
    for c in cnt[v]:
        cnt[v][c] = now_cnt[c]
    
    for c in wet[v]:
        wet[v][c] = now_wet[c]
    
    for nv, c, w in G[v]:
        if nv==pv:
            continue
        
        now_cnt[c] += 1
        now_wet[c] += w
        dfs(nv, v, now_cnt, now_wet)
        now_cnt[c] -= 1
        now_wet[c] -= w

N, Q = map(int, input().split())
G = [[] for _ in range(N)]

for _ in range(N-1):
    a, b, c, d = map(int, input().split())
    G[a-1].append((b-1, c, d))
    G[b-1].append((a-1, c, d))
    
log_size = 20
parent = [[-1]*N for _ in range(log_size)]
dep, dist = bfs()

for i in range(1, log_size):
    for v in range(N):
        if parent[i-1][v]>=0:
            parent[i][v] = parent[i-1][parent[i-1][v]]

xyuv = [tuple(map(int, input().split())) for _ in range(Q)]
cnt = defaultdict(lambda: defaultdict(int))
wet = defaultdict(lambda: defaultdict(int))

for x, y, u, v in xyuv:
    cnt[u-1][x] = -1
    cnt[v-1][x] = -1
    cnt[lca(u-1, v-1)][x] = -1
    wet[u-1][x] = -1
    wet[v-1][x] = -1
    wet[lca(u-1, v-1)][x] = -1

dfs(0, -1, defaultdict(int), defaultdict(int))

for x, y, u, v in xyuv:
    l = lca(u-1, v-1)
    decr = wet[u-1][x]+wet[v-1][x]-2*wet[l][x]
    incr = y*(cnt[u-1][x]+cnt[v-1][x]-2*cnt[l][x])
    print(calc_dist(u-1, v-1)-decr+incr)

Submission status: Accepted

Solution code:
import sys
input = sys.stdin.buffer.readline
sys.setrecursionlimit(10 ** 7)

N, Q = map(int, input().split())
D = N.bit_length()

edge = [[] for _ in range(N)]
for _ in range(N - 1):
    a, b, c, d = map(int, input().split())
    a -= 1
    b -= 1
    edge[a].append((b, c, d))
    edge[b].append((a, c, d))
query = tuple(tuple(map(int, input().split())) for _ in range(Q))

par = [[-1] * N for _ in range(D)]
dep = [0] * N
dist = [0] * N
node = [0]
while node:
    s = node.pop()
    for t, _, d in edge[s]:
        if par[0][s] == t:
            continue
        par[0][t] = s
        dep[t] = dep[s] + 1
        dist[t] = dist[s] + d
        node.append(t)

for d in range(D - 1):
    for i in range(N):
        par[d + 1][i] = par[d][par[d][i]]


def LCA(x, y):
    if dep[x] < dep[y]:
        x, y = y, x
    h = dep[x] - dep[y]
    for d in reversed(range(D)):
        if h >= 1 << d:
            x = par[d][x]
            h -= 1 << d
    if x == y:
        return x
    for d in reversed(range(D)):
        if par[d][x] != par[d][y]:
            x = par[d][x]
            y = par[d][y]
    return par[0][x]


memo = [[] for _ in range(N)]
ans = [0] * Q

for i, (x, y, u, v) in enumerate(query):
    u -= 1
    v -= 1
    c = LCA(u, v)
    ans[i] = dist[u] + dist[v] - 2 * dist[c]
    memo[u].append((x, y, i))
    memo[v].append((x, y, i))
    memo[c].append((x, y, ~i))


c_len = [0] * (N + 1)
c_cnt = [0] * (N + 1)


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


dfs(0)
print(*ans, sep="\n")

Submission status: Accepted

Solution code:
import sys
read = sys.stdin.buffer.read
readline = sys.stdin.buffer.readline
readlines = sys.stdin.buffer.readlines

n,q = map(int,readline().split())
data = list(map(int,read().split()))
abcd = data[:4*(n-1)]
xyuv = data[4*(n-1):]

root = 1
dbl_max = 20
dbl = [[0] * dbl_max for _ in range(n+1)]
depth = [0] * (n+1)
links = [[] for _ in range(n+1)]
it = iter(abcd)
for a,b,c,d in zip(it,it,it,it):
    links[a].append((b,c,d))
    links[b].append((a,c,d))

stack = [root]
while(stack):
    i = stack.pop()
    for j,c,d in links[i]:
        if(j==dbl[i][0]):
            continue
        dbl[j][0] = i
        depth[j] = depth[i]+1
        stack.append(j)

for i in range(1,dbl_max):
    for j in range(1,n+1):
        dbl[j][i] = dbl[dbl[j][i-1]][i-1]

def get_lca(x,y):
    if(depth[x] > depth[y]):
        x,y = y,x

    dif = depth[y]-depth[x]
    for i in range(dbl_max):
        if(dif==0):
            break
        if(dif>>i)&1:
            y = dbl[y][i]
            dif -= (1<<i)

    for i in range(dbl_max-1,-1,-1):
        if(dbl[x][i] != dbl[y][i]):
            x = dbl[x][i]
            y = dbl[y][i]

    if(x!=y):
        return dbl[x][0]
    else:
        return x

ans = [0] * q
query = [[] for _ in range(n+1)]
for i in range(q):
    x,y,u,v = xyuv[i*4:i*4+4]

    lca = get_lca(u,v)
    query[u].append((i,x,y,1))
    query[v].append((i,x,y,1))
    query[lca].append((i,x,y,-2))

tot = 0
c_cost = [[0,0] for _ in range(n)]
stack = []
for j,c,d in links[root]:
    stack.append((root,j,c,d,1))

while(stack):
    a,b,c,d,num = stack.pop()
    tot += d*num
    c_cost[c][0] += d*num
    c_cost[c][1] += num

    if(num==1):
        for qi,x,y,num_q in query[b]:
            ans[qi] += num_q * (tot - c_cost[x][0] + c_cost[x][1]*y)

        next = []
        for j,c,d in links[b]:
            if(j==dbl[b][0]):
                stack.append((b,j,c,d,-1))
            else:
                next.append((b,j,c,d,1))
        stack += next
    # print(c_cost)

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

Submission status: Accepted

Solution code:
# -*- coding: utf-8 -*-
import sys
from collections import defaultdict
sys.setrecursionlimit(10**9)
INF=10**18
MOD=10**9+7
input=lambda: sys.stdin.readline().rstrip()
YesNo=lambda b: bool([print('Yes')] if b else print('No'))
YESNO=lambda b: bool([print('YES')] if b else print('NO'))
int1=lambda x:int(x)-1

N,Q=map(int,input().split())
edge=[[] for _ in range(N)]
for _ in range(N-1):
    a,b,c,d=map(int1,input().split())
    d+=1
    edge[a].append((b,c,d))
    edge[b].append((a,c,d))
U=defaultdict(list)
V=defaultdict(list)
Query=[]
for i in range(Q):
    x,y,u,v=map(int1,input().split())
    y+=1
    Query.append((x,y,u,v))
    U[u].append((x,y,i))
    V[v].append((x,y,i))

S=[0]*(2*N-1)

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

et,left,depth=EulerTour(0,N,edge)

class SegmentTree:
    def __init__(self,n,segfunc,ide_ele):
        self.segfunc=segfunc
        self.ide_ele=ide_ele
        self.num=2**(n-1).bit_length()
        self.dat=[ide_ele]*2*self.num
    
    def init(self,iter):
        for i in range(len(iter)):
            self.dat[i+self.num]=iter[i]
        for i in range(self.num-1,0,-1):
            self.dat[i]=self.segfunc(self.dat[i*2],self.dat[i*2+1])
    
    def update(self,k,x):
        k+=self.num
        self.dat[k]=x
        while k:
            k//=2
            self.dat[k]=self.segfunc(self.dat[k*2],self.dat[k*2+1])
    
    def query(self,p,q):
        if q<=p:
            return self.ide_ele
        p+=self.num
        q+=self.num-1
        res=self.ide_ele
        while q-p>1:
            if p&1==1:
                res=self.segfunc(res,self.dat[p])
            if q&1==0:
                res=self.segfunc(res,self.dat[q])
                q-=1
            p=(p+1)//2
            q=q//2
        if p==q:
            res=self.segfunc(res,self.dat[p])
        else:
            res=self.segfunc(self.segfunc(res,self.dat[p]),self.dat[q])
        return res

s=SegmentTree(2*N-1,lambda a,b:min(a,b,key=lambda t:t[0]),(INF,INF))
for i in range(2*N-1):
    s.update(i,(depth[i],i))

LCA=defaultdict(list)
LLCA=[-1]*Q
for i,(x,y,u,v) in enumerate(Query):
    p,q=min(left[u],left[v]),max(left[u],left[v])+1
    res=s.query(p,q)
    LCA[et[res[1]]].append((x,y,i))
    LLCA[i]=res[1]

diff=[0]*Q

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

EulerTour2(0,N,edge)

for i,(x,y,u,v) in enumerate(Query):
    print(S[left[v]]+S[left[u]]-S[LLCA[i]]*2+diff[i])