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:
import sys
import random
random.seed(1)
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)]
        for i, item in enumerate(a):
            self.update(i, item)

    # 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.edge = edge[:]
        self.path = []
        self.depth = []
        self.color = []
        self.weight = []
        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.append(v)
        self.depth.append(d)
        self.color.append((c, 1))
        self.weight.append(w)
        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.append(v)
            self.depth.append(d)
            self.color.append((c, -1))
            self.weight.append(-w)
            k += 1
        return k
        
n, q = [int(item) for item in input().split()] 
edge = [[] for _ in range(n)]

for i 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 i in range(q):
    x, y, u, v = [int(item) for item in input().split()]
    u -= 1; v -= 1
    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, sign = c
    colors[color] += sign 
    c_depth[color] += w
    depth += w

    for key in memo[p].keys():
        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([str(item) for item in ans]))

Submission status: Time Limit Exceeded

Solution code:
import sys
import random
random.seed(1)
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.edge = edge[:]
        self.path = []
        self.depth = []
        self.color = []
        self.weight = []
        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.append(v)
        self.depth.append(d)
        self.color.append((c, 1))
        self.weight.append(w)
        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.append(v)
            self.depth.append(d)
            self.color.append((c, -1))
            self.weight.append(-w)
            k += 1
        return k
        
n, q = [int(item) for item in input().split()] 
edge = [[] for _ in range(n)]

for i 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, random.randint(0, n-1))
memo = [dict() for _ in range(n)]
lca_memo = dict()

query = []
for i in range(q):
    x, y, u, v = [int(item) for item in input().split()]
    u -= 1; v -= 1
    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, sign = c
    colors[color] += sign 
    c_depth[color] += w
    depth += w

    for key in memo[p].keys():
        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([str(item) for item in ans]))

Submission status: Time Limit Exceeded

Solution code:
import sys
import random
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.edge = edge[:]
        self.path = []
        self.depth = []
        self.color = []
        self.weight = []
        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.append(v)
        self.depth.append(d)
        self.color.append((c, 1))
        self.weight.append(w)
        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.append(v)
            self.depth.append(d)
            self.color.append((c, -1))
            self.weight.append(-w)
            k += 1
        return k
        
n, q = [int(item) for item in input().split()] 
edge = [[] for _ in range(n)]

for i 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 i in range(q):
    x, y, u, v = [int(item) for item in input().split()]
    u -= 1; v -= 1
    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, sign = c
    colors[color] += sign 
    c_depth[color] += w
    depth += w

    for key in memo[p].keys():
        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([str(item) for item in ans]))

Submission status: Time Limit Exceeded

Solution code:
import sys
import random
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.edge = edge[:]
        self.path = []
        self.depth = []
        self.color = []
        self.weight = []
        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.append(v)
        self.depth.append(d)
        self.color.append(c)
        self.weight.append(w)
        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.append(v)
            self.depth.append(d)
            self.color.append(-c)
            self.weight.append(-w)
            k += 1
        return k
        
n, q = [int(item) for item in input().split()] 
edge = [[] for _ in range(n)]

for i 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 i in range(q):
    x, y, u, v = [int(item) for item in input().split()]
    u -= 1; v -= 1
    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():
        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([str(item) for item in ans]))

Submission status: Time Limit Exceeded

Solution code:
import sys
import random
sys.setrecursionlimit(10**6)
input = sys.stdin.readline

INF = 10**9

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.edge = edge[:]
        self.path = []
        self.depth = []
        self.color = []
        self.weight = []
        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 get_depth(self, v):
        return self.depth[self.index[v]]

    def euler_tour(self, prev, v, d, k, c, w):
        self.index[v] = k
        self.path.append(v)
        self.depth.append(d)
        self.color.append(c)
        self.weight.append(w)
        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.append(v)
            self.depth.append(d)
            self.color.append(-c)
            self.weight.append(-w)
            k += 1
        return k
        
n, q = [int(item) for item in input().split()] 
edge = [[] for _ in range(n)]

for i 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, random.randint(0,n-1))
memo = [dict() for _ in range(n)]
lca_memo = dict()

query = []
for i in range(q):
    x, y, u, v = [int(item) for item in input().split()]
    u -= 1; v -= 1
    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 i, (p, c, w) in enumerate(zip(lca.path, lca.color, lca.weight)):
    if i == 0:
        continue
    else:
        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():
            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([str(item) for item in ans]))

Submission status: Time Limit Exceeded

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

INF = 10**9

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.edge = edge[:]
        self.path = []
        self.depth = []
        self.color = []
        self.weight = []
        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 get_depth(self, v):
        return self.depth[self.index[v]]

    def euler_tour(self, prev, v, d, k, c, w):
        self.index[v] = k
        self.path.append(v)
        self.depth.append(d)
        self.color.append(c)
        self.weight.append(w)
        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.append(v)
            self.depth.append(d)
            self.color.append(-c)
            self.weight.append(-w)
            k += 1
        return k
        
n, q = [int(item) for item in input().split()] 
edge = [[] for _ in range(n)]

for i 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 i in range(q):
    x, y, u, v = [int(item) for item in input().split()]
    u -= 1; v -= 1
    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 i, (p, c, w) in enumerate(zip(lca.path, lca.color, lca.weight)):
    if i == 0:
        continue
    else:
        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():
            memo[p][key] = (colors[key], c_depth[key])
        memo[p][0] = depth 

for x, y, ca, u, v in query:
    # Calc basic distance
    ans = memo[u][0] + memo[v][0] - memo[ca][0] * 2
    # Append color diff
    ans += (memo[v][x][0] + memo[u][x][0] - memo[ca][x][0] * 2) * y 
    ans -= (memo[v][x][1] + memo[u][x][1] - memo[ca][x][1] * 2)
    print(ans)

Submission status: Time Limit Exceeded

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

INF = 10**9

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.edge = edge[:]
        self.path = []
        self.depth = []
        self.color = []
        self.weight = []
        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 get_depth(self, v):
        return self.depth[self.index[v]]

    def euler_tour(self, prev, v, d, k, c, w):
        self.index[v] = k
        self.path.append(v)
        self.depth.append(d)
        self.color.append(c)
        self.weight.append(w)
        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.append(v)
            self.depth.append(d)
            self.color.append(-c)
            self.weight.append(-w)
            k += 1
        return k
        
n, q = [int(item) for item in input().split()] 
edge = [[] for _ in range(n)]

for i 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 i in range(q):
    x, y, u, v = [int(item) for item in input().split()]
    u -= 1; v -= 1
    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 i, (p, c, w) in enumerate(zip(lca.path, lca.color, lca.weight)):
    if i == 0:
        continue
    else:
        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():
            memo[p][key] = (colors[key], c_depth[key])
        memo[p][0] = depth 

for x, y, ca, u, v in query:
    # Calc basic distance
    ans = memo[u][0] + memo[v][0] - memo[ca][0] * 2
    # Append color diff
    ans += (memo[v][x][0] + memo[u][x][0] - memo[ca][x][0] * 2) * y 
    ans -= (memo[v][x][1] + memo[u][x][1] - memo[ca][x][1] * 2)
    print(ans)

Submission status: Time Limit Exceeded

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

INF = 10**9

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.edge = edge[:]
        self.path = []
        self.depth = []
        self.color = []
        self.weight = []
        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 get_depth(self, v):
        return self.depth[self.index[v]]

    def euler_tour(self, prev, v, d, k, c, w):
        self.index[v] = k
        self.path.append(v)
        self.depth.append(d)
        self.color.append(c)
        self.weight.append(w)
        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.append(v)
            self.depth.append(d)
            self.color.append(-c)
            self.weight.append(-w)
            k += 1
        return k
        
n, q = [int(item) for item in input().split()] 
edge = [[] for _ in range(n)]

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

query = []
for i in range(q):
    x, y, u, v = [int(item) for item in input().split()]
    u -= 1; v -= 1
    ca = lca.get_lca(u, v)
    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 i, (p, c, w) in enumerate(zip(lca.path, lca.color, lca.weight)):
    if i == 0:
        continue
    else:
        if c < 0:
            colors[abs(c)] -= 1
        else:
            colors[abs(c)] += 1
        c_depth[abs(c)] += w
        depth += w

        for key in memo[p].keys():
            memo[p][key] = (colors[key], c_depth[key])
        memo[p][0] = depth 

for x, y, ca, u, v in query:
    # Calc basic distance
    ans = memo[u][0] + memo[v][0] - memo[ca][0] * 2
    # Append color diff
    ans += (memo[v][x][0] + memo[u][x][0] - memo[ca][x][0] * 2) * y 
    ans -= (memo[v][x][1] + memo[u][x][1] - memo[ca][x][1] * 2)
    print(ans)