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: Time Limit Exceeded

Solution code:
from collections import deque
from copy import deepcopy

class EulerTour:
    def __init__(self, N):
        self.N = N
        self.G = [[] for _ in range(N)]
    
    def add_edge(self, a, b):
        self.G[a].append(b)
        self.G[b].append(a)
    
    def from_graph(self, G, copy=False):
        if copy: self.G = deepcopy(G)
        else: self.G = G
            
    def build(self, root=0, lca=True):
        self.root = root
        self._dfs(root)
        self.In = [0]*self.N
        self.Out = [0]*self.N
        for i, v in enumerate(self.node_list):
            if self.In[v] == 0:
                self.In[v] = i
            self.Out[v] = i
        if lca:
            self.seg = SegTree(len(self.node_list))
            self.seg.build([self.d[self.node_list[i]] for i in range(len(self.node_list))])
    
    def lca(self, a, b):
        if self.root in (a,b):
            return self.root
        a_out, b_in = self.Out[a], self.In[b]
        if a_out > b_in:
            a_out, b_in = self.Out[b], self.In[a]
        if a_out > b_in:
            a_out, b_in = self.Out[b], self.Out[a]
        if a_out > b_in:
            a_out, b_in = self.Out[a], self.Out[b]
        idx = self.seg.find(a_out, b_in+1)
        return self.node_list[idx]
            
    def _dfs(self, root=0):
        self.idx2edge = {}
        self.edge2idx = {}
        self.node_list = []
        self.edge_list = []
        seen = [0]*len(self.G)
        prev = [0]*len(self.G)
        self.d =[-1]*len(self.G)
        todo = [~root, root]
        count = 0
        while todo:
            a = todo.pop()
            if a >= 0:
                seen[a] = 1
                self.d[a] = self.d[prev[a]] + 1
                edge = (prev[a], a)
                self.idx2edge[count] = edge
                self.edge2idx[edge] = count
                self.edge_list.append(count)
                self.node_list.append(a)
                count += 1
                for b in reversed(self.G[a]):
                    if seen[b]: continue
                    todo.append(~b)
                    todo.append(b)
                    prev[b] = a
            else: 
                edge = (prev[~a], ~a)
                idx = self.edge2idx[edge]
                self.edge_list.append(-idx)
                if ~a != 0: self.node_list.append(prev[~a])
                    
class SegTree:
    """ segment tree with point modification and range product. """
    # # https://yukicoder.me/submissions/452850
    def __init__(self, N, data_f = min, data_unit=1<<30):
        self.N = N
        self.data_f = data_f
        self.data_unit = data_unit
        self.data = [self.data_unit] * (N + N)

    def build(self, raw_data):
        data = self.data
        f = self.data_f
        N = self.N
        data[N:] = raw_data[:]
        for i in range(N - 1, 0, -1):
            data[i] = f(data[i << 1], data[i << 1 | 1])

    def set_val(self, i, x):
        data = self.data
        f = self.data_f
        i += self.N
        data[i] = x
        while i > 1:
            data[i >> 1] = f(data[i], data[i ^ 1])
            i >>= 1

    def fold(self, L, R):
        """ compute for [L, R) """
        vL = vR = self.data_unit
        data = self.data
        f = self.data_f
        L += self.N
        R += self.N
        while L < R:
            if L & 1:
                vL = f(vL, data[L])
                L += 1
            if R & 1:
                R -= 1
                vR = f(data[R], vR)
            L >>= 1
            R >>= 1
        return f(vL, vR)
    
    def find(self, L, R):
        x = self.fold(L,R)
        while R-L>1:
            M = (R+L)//2
            if self.fold(L,M)==x: R = M
            else: L = M
        return L
      
N,Q = map(int,input().split())
G = [{} for _ in range(N)]
et = EulerTour(N)
for i in range(N-1):
  a,b,c,d = map(int,input().split())
  a,b = a-1,b-1
  G[a][b] = (c,d)
  G[b][a] = (c,d)
  et.add_edge(a,b)

def bfs(s):
    seen = [0]*N
    c = [{} for _ in range(N)]
    d = [0]*N
    todo = deque()
    seen[s]=1
    todo.append(s)
    while todo:
      a = todo.popleft()
      for b in G[a].keys():
        if seen[b] == 0:
          seen[b] = 1
          todo.append(b)
          d_ = G[a][b][1]
          d[b] = d[a] + d_
          c[b] = deepcopy(c[a])
          c_ = G[a][b][0]
          if c[b].get(c_): c[b][c_].append(d_)
          else: c[b][c_] = [d_]
    return c,d
  
root = 0
et.build(root)
c,d = bfs(root)
for i in range(Q):
  x,y,u,v = map(int,input().split())
  u,v = u-1,v-1
  w = et.lca(u,v)
  set1 = (c[u][x] if c[u].get(x) else {})
  set2 = (c[v][x] if c[v].get(x) else {})
  set3 = (c[w][x] if c[w].get(x) else {})
  diff1 = len(set1)*y - sum(set1)
  diff2 = len(set2)*y - sum(set2)
  diff3 = len(set3)*y - sum(set3)
  print(d[u]+diff1 - (d[w]+diff3) + d[v]+diff2 - (d[w]+diff3))

Submission status: Time Limit Exceeded

Solution code:
from sys import setrecursionlimit
setrecursionlimit(10 ** 6)

from networkx import DiGraph, lowest_common_ancestor

N, Q, *I = map(int, open(0).read().split())
ABCD, XYUV = I[:4 * (N - 1)], I[4 * (N - 1):]

E = [[] for _ in range(N)]
for a, b, c, d in zip(*[iter(ABCD)] * 4):
    E[a - 1].append((b - 1, c, d))
    E[b - 1].append((a - 1, c, d))

dep = [0] * N
dist = [0] * N
G = DiGraph()

S = [(0, -1)]
while S:
    cur, par = S.pop()
    for nxt, _, d in E[cur]:
        if nxt == par:
            continue

        G.add_edge(cur, nxt)
        dep[nxt] = 1 + dep[cur]
        dist[nxt] = d + dist[cur]
        S.append((nxt, cur))

def lca(u, v):
    return lowest_common_ancestor(G, u, v)

memo = [[] for _ in range(N)]
ans = []
for i, (x, y, u, v) in enumerate(zip(*[iter(XYUV)] * 4)):
    u -= 1
    v -= 1

    c = lca(u, v)

    ans.append(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_sum = [0] * (N + 1)
c_num = [0] * (N + 1)
def solve(cur, par=-1):
    for c, val, i in memo[cur]:
        if i >= 0:
            ans[i] += c_num[c] * val - c_sum[c]
        else:
            ans[~i] += -2 * (c_num[c] * val - c_sum[c])

    for nxt, c, d in E[cur]:
        if nxt == par:
            continue

        c_sum[c] += d
        c_num[c] += 1
        solve(nxt, cur)
        c_num[c] -= 1
        c_sum[c] -= d
solve(0)

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

Submission status: Time Limit Exceeded

Solution code:
from sys import setrecursionlimit
setrecursionlimit(10 ** 6)

from networkx import DiGraph, lowest_common_ancestor

N, Q, *I = map(int, open(0).read().split())
ABCD, XYUV = I[:4 * (N - 1)], I[4 * (N - 1):]

E = [[] for _ in range(N)]
for a, b, c, d in zip(*[iter(ABCD)] * 4):
    E[a - 1].append((b - 1, c, d))
    E[b - 1].append((a - 1, c, d))

dep = [0] * N
dist = [0] * N
G = DiGraph()

S = [(0, -1)]
while S:
    cur, par = S.pop()
    for nxt, _, d in E[cur]:
        if nxt == par:
            continue

        G.add_edge(cur, nxt)
        dep[nxt] = 1 + dep[cur]
        dist[nxt] = d + dist[cur]
        S.append((nxt, cur))

def lca(u, v):
    return lowest_common_ancestor(G, u, v)

memo = [[] for _ in range(N)]
ans = []
for i, (x, y, u, v) in enumerate(zip(*[iter(XYUV)] * 4)):
    u -= 1
    v -= 1

    c = lca(u, v)

    ans.append(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_sum = [0] * (N + 1)
c_num = [0] * (N + 1)
def solve(cur, par=-1):
    for c, val, i in memo[cur]:
        if i >= 0:
            ans[i] += c_num[c] * val - c_sum[c]
        else:
            ans[~i] += -2 * (c_num[c] * val - c_sum[c])

    for nxt, c, d in E[cur]:
        if nxt == par:
            continue

        c_sum[c] += d
        c_num[c] += 1
        solve(nxt, cur)
        c_num[c] -= 1
        c_sum[c] -= d
solve(0)

for a in ans:
    print(a)

Submission status: Time Limit Exceeded

Solution code:
from sys import setrecursionlimit
setrecursionlimit(10 ** 6)

from networkx import DiGraph, lowest_common_ancestor

N, Q, *I = map(int, open(0).read().split())
ABCD, XYUV = I[:4 * (N - 1)], I[4 * (N - 1):]

E = [[] for _ in range(N)]
for a, b, c, d in zip(*[iter(ABCD)] * 4):
    E[a - 1].append((b - 1, c, d))
    E[b - 1].append((a - 1, c, d))

dep = [0] * N
dist = [0] * N
G = DiGraph()

S = [(0, -1)]
while S:
    cur, par = S.pop()
    for nxt, _, d in E[cur]:
        if nxt == par:
            continue

        G.add_edge(cur, nxt)
        dep[nxt] = 1 + dep[cur]
        dist[nxt] = d + dist[cur]
        S.append((nxt, cur))

memo = [[] for _ in range(N)]
ans = []
for i, (x, y, u, v) in enumerate(zip(*[iter(XYUV)] * 4)):
    u -= 1
    v -= 1

    c = lowest_common_ancestor(G, u, v)

    ans.append(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_sum = [0] * (N + 1)
c_num = [0] * (N + 1)
def solve(cur, par=-1):
    for c, val, i in memo[cur]:
        if i >= 0:
            ans[i] += c_num[c] * val - c_sum[c]
        else:
            ans[~i] += -2 * (c_num[c] * val - c_sum[c])

    for nxt, c, d in E[cur]:
        if nxt == par:
            continue

        c_sum[c] += d
        c_num[c] += 1
        solve(nxt, cur)
        c_num[c] -= 1
        c_sum[c] -= d
solve(0)

for a in ans:
    print(a)

Submission status: Time Limit Exceeded

Solution code:
from sys import setrecursionlimit
setrecursionlimit(10 ** 6)

from networkx import DiGraph, lowest_common_ancestor

def main():
    N, Q, *I = map(int, open(0).read().split())
    ABCD, XYUV = I[:4 * (N - 1)], I[4 * (N - 1):]

    E = [[] for _ in range(N)]
    for a, b, c, d in zip(*[iter(ABCD)] * 4):
        E[a - 1].append((b - 1, c, d))
        E[b - 1].append((a - 1, c, d))

    dep = [0] * N
    dist = [0] * N
    G = DiGraph()
    def dfs(cur, par=-1):
        for nxt, _, d in E[cur]:
            if nxt == par:
                continue

            G.add_edge(cur, nxt)
            dep[nxt] = 1 + dep[cur]
            dist[nxt] = d + dist[cur]
            dfs(nxt, cur)
    dfs(0)

    memo = [[] for _ in range(N)]
    ans = []
    for i, (x, y, u, v) in enumerate(zip(*[iter(XYUV)] * 4)):
        u -= 1
        v -= 1

        c = lowest_common_ancestor(G, u, v)

        ans.append(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_sum = [0] * (N + 1)
    c_num = [0] * (N + 1)
    def solve(cur, par=-1):
        for c, val, i in memo[cur]:
            if i >= 0:
                ans[i] += c_num[c] * val - c_sum[c]
            else:
                ans[~i] += -2 * (c_num[c] * val - c_sum[c])

        for nxt, c, d in E[cur]:
            if nxt == par:
                continue

            c_sum[c] += d
            c_num[c] += 1
            solve(nxt, cur)
            c_num[c] -= 1
            c_sum[c] -= d
    solve(0)

    for a in ans:
        print(a)

main()

Submission status: Time Limit Exceeded

Solution code:
from sys import setrecursionlimit
setrecursionlimit(10 ** 6)

from networkx import DiGraph, lowest_common_ancestor

N, Q, *I = map(int, open(0).read().split())
ABCD, XYUV = I[:4 * (N - 1)], I[4 * (N - 1):]

E = [[] for _ in range(N)]
for a, b, c, d in zip(*[iter(ABCD)] * 4):
    E[a - 1].append((b - 1, c, d))
    E[b - 1].append((a - 1, c, d))

dep = [0] * N
dist = [0] * N
G = DiGraph()
def dfs(cur, par=-1):
    for nxt, _, d in E[cur]:
        if nxt == par:
            continue

        G.add_edge(cur, nxt)
        dep[nxt] = 1 + dep[cur]
        dist[nxt] = d + dist[cur]
        dfs(nxt, cur)
dfs(0)

memo = [[] for _ in range(N)]
ans = []
for i, (x, y, u, v) in enumerate(zip(*[iter(XYUV)] * 4)):
    u -= 1
    v -= 1

    c = lowest_common_ancestor(G, u, v)

    ans.append(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_sum = [0] * (N + 1)
c_num = [0] * (N + 1)
def solve(cur, par=-1):
    for c, val, i in memo[cur]:
        if i >= 0:
            ans[i] += c_num[c] * val - c_sum[c]
        else:
            ans[~i] += -2 * (c_num[c] * val - c_sum[c])

    for nxt, c, d in E[cur]:
        if nxt == par:
            continue

        c_sum[c] += d
        c_num[c] += 1
        solve(nxt, cur)
        c_num[c] -= 1
        c_sum[c] -= d
solve(0)

for a in ans:
    print(a)

Submission status: Time Limit Exceeded

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 = [0]*n
weight = [0]*n

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: Time Limit Exceeded

Solution code:
import sys
sys.setrecursionlimit(10**5+5)
from collections import defaultdict
input = sys.stdin.readline
def dfs(now,par):
    for nex in e[now]:
        if nex[0] == par:
            continue
        depth[nex[0]] = depth[now]+1
        dis[nex[0]] = dis[now]+nex[2]
        lca[0][nex[0]] = now
        dfs(nex[0],now)

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

dfs(1,0)
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: Time Limit Exceeded

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

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 = n.bit_length()
lca = [[0]*size for i in range(bitlen)]
depth = [-1]*size
dis = [-1]*size
depth[1] = 0
dis[1] = 0
def dfs(now,par):
    for nex in e[now]:
        if nex[0] == par:
            continue
        depth[nex[0]] = depth[now]+1
        dis[nex[0]] = dis[now]+nex[2]
        lca[0][nex[0]] = now
        dfs(nex[0],now)

dfs(1,0)
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))

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
    
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: Time Limit Exceeded

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

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 = n.bit_length()
lca = [[0]*bitlen for i in range(size)]
depth = [-1]*size
dis = [-1]*size
depth[1] = 0
dis[1] = 0
def dfs(now,par):
    for nex in e[now]:
        if nex[0] == par:
            continue
        depth[nex[0]] = depth[now]+1
        dis[nex[0]] = dis[now]+nex[2]
        lca[nex[0]][0] = now
        dfs(nex[0],now)

dfs(1,0)
for i in range(1,bitlen):
    for j in range(1,size):
        if lca[j][i-1] > 0:
            lca[j][i] = lca[lca[j][i-1]][i-1]
    
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[x][s.bit_length()-1]
        dif -= s
    if x == y:
        return x
    for i in range(bitlen-1,-1,-1):
        if lca[x][i] != lca[y][i]:
            x = lca[x][i]
            y = lca[y][i]
    return lca[x][0]


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

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
    
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: Time Limit Exceeded

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)

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

    return x

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

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

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

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

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

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

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

Submission status: Time Limit Exceeded

Solution code:
import sys
from bisect import bisect
sys.setrecursionlimit(10**6)
def input():
	return sys.stdin.buffer.readline()[:-1]

class LCA():
	def __init__(self, adj_given, root_given):
		self.adj = adj = adj_given
		self.root = root_given
		self.n = n = len(adj)
		self.maxlog = 0
		while (1 << self.maxlog) < n:
			self.maxlog += 1
		maxlog = self.maxlog
		self.par = [[-1 for _ in range(n)] for _ in range(maxlog)]
		self.dep = [0 for _ in range(n)]
		self.num_key = [[0] for _ in range(n)]
		self.num_value = [[0] for _ in range(n)]
		self.sum_value = [[0] for _ in range(n)]
		self.first = [10**6 for _ in range(n)]
		self.first[self.root] = 0
		self.visited = 1
		self.cnt = 1
		self.dist = [0 for _ in range(n)]

	def dfs(self, x, pa, de, co, le):
		par = self.par
		dep = self.dep
		root = self.root
		
		par[0][x] = pa
		dep[x] = de

		if x != root and self.visited < n:
			self.num_key[co].append(self.cnt)
			self.num_value[co].append(self.num_value[co][-1] + 1)
			self.sum_value[co].append(self.sum_value[co][-1] + le)
			if self.first[x] == 10**6:
				self.visited += 1
				self.first[x] = self.cnt
			self.cnt += 1
			self.dist[x] = self.dist[pa] + le

		for v, c, l in adj[x]:
			if v != pa:
				self.dfs(v, x, de+1, c, l)

		if x != root and self.visited < n:
			self.num_key[co].append(self.cnt)
			self.num_value[co].append(self.num_value[co][-1] - 1)
			self.sum_value[co].append(self.sum_value[co][-1] - le)
			self.cnt += 1

		return

	def calc(self):
		root = self.root
		par = self.par
		maxlog = self.maxlog
		self.dfs(root, -1, 0, -1, 0)

		for k in range(maxlog-1):
			for v in range(n):
				if par[k][v] < 0:
					continue
				else:
					par[k+1][v] = par[k][par[k][v]]
		return

	def get_lca(self, u, v):
		par = self.par
		dep = self.dep
		maxlog = self.maxlog
		if dep[u] > dep[v]:
			u, v = v, u
		for k in range(maxlog):
			if ((dep[v] - dep[u]) >> k) & 1:
				v = par[k][v]

		if u == v:
			return u
		else:
			for k in range(maxlog-1, -1, -1):
				if par[k][u] != par[k][v]:
					u = par[k][u]
					v = par[k][v]
			return par[0][u]

n, q = map(int, input().split())
adj = [[] for _ in range(n)]
for _ in range(n-1):
	a, b, c, d = map(int, input().split())
	adj[a-1].append((b-1, c-1, d))
	adj[b-1].append((a-1, c-1, d))

lca = LCA(adj, 0)
lca.calc()

for _ in range(q):
	x, y, u, v = map(int, input().split())
	x, u, v = x-1, u-1, v-1
	a = lca.get_lca(u, v)
	id_u = max(bisect(lca.num_key[x], lca.first[u])-1, 0)
	id_v = max(bisect(lca.num_key[x], lca.first[v])-1, 0)
	id_a = max(bisect(lca.num_key[x], lca.first[a])-1, 0)

	dist_u = lca.dist[u] + lca.num_value[x][id_u] * y - lca.sum_value[x][id_u]
	dist_v = lca.dist[v] + lca.num_value[x][id_v] * y - lca.sum_value[x][id_v]
	dist_a = lca.dist[a] + lca.num_value[x][id_a] * y - lca.sum_value[x][id_a]
	print(dist_u + dist_v - 2 * dist_a)

Submission status: Time Limit Exceeded

Solution code:
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):
#   global dist
#   for i, x, y, b in C[v]:
#     A[i] += (dist + num[x]*y - cum[x]) * b
#   for nv, c, d in edge[v]:
#     c+=1
#     if nv ==pars[v]:continue
#     dist += d
#     num[c] += 1
#     cum[c] += d
#     dfs2(nv)
#     dist -= d
#     num[c] -= 1
#     cum[c] -= d
# dfs2(0)
# print(*A, sep="\n")