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.

Solution in C# (Mono 4.6.2.0) (judge verdict: Accepted)

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading.Tasks;

class TEST{
	static void Main(){
		Sol mySol =new Sol();
		mySol.Solve();
	}
}

class Sol{
	const int SZ = 330;
	//const int SZ = 10;
	public void Solve(){
		
		E = new List<int>[N];
		for(int i=0;i<N;i++){
			E[i] = new List<int>();
		}
		for(int i=0;i<N-1;i++){
			E[A[i]].Add(i);
			E[B[i]].Add(i);
		}
		Nxt = (eidx, u) => A[eidx] == u ? B[eidx] : A[eidx];
		
		root = 0;
		lca_init();
		
		// depth % SZ == SZ - 1 の点に情報を集約する
		//   SZ-先祖のpath 中の {（色，コスト）}，{（色，数）}，トータルコスト
		//   使うところだけインスタンス作るので一旦底空間は全Nで確保しておく．
		Dictionary<int,int>[] ColCost = new Dictionary<int,int>[N];
		Dictionary<int,int>[] ColCnt = new Dictionary<int,int>[N];
		int[] TotCost = new int[N];
		int[] grandP = new int[N];
		for(int i=0;i<N;i++){
			ColCost[i] = new Dictionary<int,int>();
			ColCnt[i] = new Dictionary<int,int>();
			grandP[i] = -1;
		}
		for(int i=0;i<N;i++){
			if(depth[i] % SZ != SZ - 1) continue;
			if(depth[i] == SZ - 1) continue; // 頂上バケットから上にはいかないので避けておく
			int v = i;
			int ndep = depth[i] - SZ;
			while(depth[v] > ndep){
				int color = C[pe[v]];
				int dist = D[pe[v]];
				if(!ColCost[i].ContainsKey(color)) ColCost[i].Add(color, 0);
				if(!ColCnt[i].ContainsKey(color)) ColCnt[i].Add(color, 0);
				TotCost[i] += dist;
				ColCost[i][color] += dist;
				ColCnt[i][color] += 1;
				
				v = parent[0][v];
			}
			grandP[i] = v;
		}
		int[] ans = new int[Q];
		
		for(int q=0;q<Q;q++){
		//Parallel.For(0, Q, q =>{	
			int g = lca(U[q], V[q]);
			int c0 = X[q];
			int d0 = Y[q];
			int totUV = 0;
			int gdep = depth[g] / SZ;
			for(int k=0;k<2;k++){
				int v = k == 0 ? U[q] : V[q];
				int tot = 0;
				int restGstep = depth[v] / SZ - gdep;
				while(restGstep > 0){
					if(depth[v] % SZ != SZ - 1){
						if(C[pe[v]] == c0){
							tot += d0;
						} else {
							tot += D[pe[v]];
						}
						if(depth[v] % SZ == 0) restGstep--;
						v = parent[0][v];
						continue;
					}
					
					int pTot = TotCost[v];
					if(ColCnt[v].ContainsKey(c0)){
						pTot -= ColCost[v][c0];
						pTot += ColCnt[v][c0] * d0;
					}
					tot += pTot;
					v = grandP[v];
					restGstep--;
				}
				
				while(v != g){
					if(C[pe[v]] == c0){
						tot += d0;
					} else {
						tot += D[pe[v]];
					}
					v = parent[0][v];
				}
				
				totUV += tot;
			}
			
			ans[q] = totUV;
		//});
		}
		
		Console.WriteLine(String.Join("\n",ans));
		
		
	}
	
	List<int>[] E;
	int[][] parent;
	int[] depth;
	int[] pe;
	static int lmax=24;
	int root;
	Func<int,int,int> Nxt = null;
	
	void bfs(int strt,int dep_strt){
		Queue<int> Q=new Queue<int>();
		parent[0][strt] = -1; depth[strt] = 0; pe[strt] = -1;
		Q.Enqueue(strt);
		while(Q.Count>0){
			int now = Q.Dequeue();
			int par = parent[0][now];
			foreach(int e in E[now]){
				int v = Nxt(e, now);
				if(v != par){
					parent[0][v] = now;
					depth[v] = depth[now] + 1;
					pe[v] = e;
					Q.Enqueue(v);
				}
			}
		}
	}
	void lca_init(){
		parent = new int[lmax][];
		for(int i=0;i<lmax;i++){
			parent[i] = new int[N];
		}
		depth = new int[N];
		pe = new int[N];
		//dfs(root,-1,0);//深さを数えるdfs
		bfs(root,0);//深さを数えるbfs
		
		for(int i=0;i<lmax-1;i++){
			for(int j=0;j<N;j++){
				if(parent[i][j] < 0){
					parent[i+1][j] = -1;
				}else{
					parent[i+1][j] = parent[i][parent[i][j]];
				}
			}
		}
	}
	
	int lca(int u,int v){
		if(depth[u] > depth[v]) return lca(v,u);
		for(int i=0;i<lmax;i++){
			if( (((depth[v]-depth[u])>>i)&1) >0 ){
				v=parent[i][v];
			}
		}
		if(u==v)return u;
		for(int i=lmax-1;i>=0;i--){
			if(parent[i][u]!=parent[i][v]){
				u=parent[i][u];
				v=parent[i][v];
			}
		}
		return parent[0][u];
	}
	
	
	
	
	
	int N,Q;
	int[] A,B,C,D;
	int[] X,Y,U,V;
	public Sol(){
		using(var r = new FastIn()){
			N = r.ReadInt(); Q = r.ReadInt();
			A = new int[N-1];
			B = new int[N-1];
			C = new int[N-1];
			D = new int[N-1];
			for(int i=0;i<N-1;i++){
				A[i] = r.ReadInt() - 1; B[i] = r.ReadInt() - 1; C[i] = r.ReadInt(); D[i] = r.ReadInt();
			}
			X = new int[Q];
			Y = new int[Q];
			U = new int[Q];
			V = new int[Q];
			for(int i=0;i<Q;i++){
				X[i] = r.ReadInt(); Y[i] = r.ReadInt(); U[i] = r.ReadInt() - 1; V[i] = r.ReadInt() - 1;
			}
		}
		
	}

	static String rs(){return Console.ReadLine();}
	static int ri(){return int.Parse(Console.ReadLine());}
	static long rl(){return long.Parse(Console.ReadLine());}
	static double rd(){return double.Parse(Console.ReadLine());}
	static String[] rsa(char sep=' '){return Console.ReadLine().Split(sep);}
	static int[] ria(char sep=' '){return Array.ConvertAll(Console.ReadLine().Split(sep),e=>int.Parse(e));}
	static long[] rla(char sep=' '){return Array.ConvertAll(Console.ReadLine().Split(sep),e=>long.Parse(e));}
	static double[] rda(char sep=' '){return Array.ConvertAll(Console.ReadLine().Split(sep),e=>double.Parse(e));}
}


class FastIn:IDisposable {
	int Size;
	byte[] Mem;
	int ptr;
	int rsize;
	bool unfinished;
	Stream stdin;
	void Init(int n) {
		Size = n;
		Mem = new byte[Size];
		rsize=(stdin=Console.OpenStandardInput()).Read(Mem, 0, Size);
		ptr = 0;
		unfinished=(rsize == Size);
	}
	void Next() {
		if (unfinished == false) return;
		rsize=stdin.Read(Mem, 0, Size);
		ptr = 0;
		unfinished = (rsize == Size);
	}
	
	~FastIn(){
		stdin.Dispose();
	}
	void IDisposable.Dispose(){
		stdin.Dispose();
	}
	public void Dispose(){
		stdin.Dispose();
	}
	
	public FastIn() {
		Init(100000);
	}
	public FastIn(int n) {
		Init(n);
	}
	public int ReadInt() {
		int ret = 0;
		int sig = 1;
		while (ptr < rsize && Mem[ptr] != ' ' && Mem[ptr] != '\n' && Mem[ptr] != '\r' ) {
			if(ret==0 && Mem[ptr] == '-'){
				sig *= -1; ptr++; continue;
			}
			ret = ret * 10 + Mem[ptr++] - '0';
			if (ptr == Size) Next();
		}
		while (ptr < rsize && (Mem[ptr] == ' ' || Mem[ptr] == '\n' || Mem[ptr] == '\r') ) {
			ptr++;
			if (ptr == Size) Next();
		}
		return ret*sig;
	}
	public uint ReadUint() {
		uint ret = 0;
		uint sig = 1;
		while (ptr < rsize && Mem[ptr] != ' ' && Mem[ptr] != '\n' && Mem[ptr] != '\r' ) {
			ret = ret * 10 + Mem[ptr++] - '0';
			if (ptr == Size) Next();
		}
		while (ptr < rsize && (Mem[ptr] == ' ' || Mem[ptr] == '\n' || Mem[ptr] == '\r') ) {
			ptr++;
			if (ptr == Size) Next();
		}
		return ret*sig;
	}
	public long ReadLong() {
		long ret = 0;
		long sig = 1;
		while (ptr < rsize && Mem[ptr] != ' ' && Mem[ptr] != '\n' && Mem[ptr] != '\r' ) {
			if(ret==0 && Mem[ptr] == '-'){
				sig *= -1; ptr++; continue;
			}
			ret = ret * 10 + Mem[ptr++] - '0';
			if (ptr == Size) Next();
		}
		while (ptr < rsize &&  (Mem[ptr] == ' ' || Mem[ptr] == '\n' || Mem[ptr] == '\r')  ) {
			ptr++;
			if (ptr == Size) Next();
		}
		return ret*sig;
	}
	
	public double ReadDouble() {
		double ret = 0;
		double sig = 1;
		bool dot = false;
		double keta = 0.1;
		while (ptr < rsize && Mem[ptr] != ' ' && Mem[ptr] != '\n' && Mem[ptr] != '\r' ) {
			if(ret==0 && Mem[ptr] == '-'){
				sig *= -1; ptr++;
				if (ptr == Size) Next();
				continue;
			}
			if(Mem[ptr] == '.'){
				dot = true;
				ptr++;
				if (ptr == Size) Next();
				continue;
			}
			if(!dot){
				ret = ret * 10 + Mem[ptr++] - '0';
				if (ptr == Size) Next();
			}else{
				ret = ret + (Mem[ptr++] - '0')*keta;
				keta /= 10.0;
				if (ptr == Size) Next();
			}
		}
		while (ptr < rsize && (Mem[ptr] == ' ' || Mem[ptr] == '\n' || Mem[ptr] == '\r') ) {
			ptr++;
			if (ptr == Size) Next();
		}
		return ret*sig;
	}

	public String ReadStr() {
		//2byte文字はNG
		StringBuilder sb = new StringBuilder();
		while (ptr < rsize && Mem[ptr] != ' ' && Mem[ptr] != '\n' && Mem[ptr] != '\r') {
			sb.Append((char)Mem[ptr++]);
			if (ptr == Size && unfinished) Next();
		}
		while (ptr < rsize &&  (Mem[ptr] == ' ' || Mem[ptr] == '\n' || Mem[ptr] == '\r') ) {
			ptr++;
			if (ptr == Size && unfinished) Next();
		}
		return sb.ToString();
	}
	public String ReadLine() {
		//極力使わない(split/parseするくらいなら上をつかう）
		StringBuilder sb = new StringBuilder();
		while (ptr < rsize && Mem[ptr] != '\n' && Mem[ptr] != '\r') {
			sb.Append((char)Mem[ptr++]);
			if (ptr == Size && unfinished) Next();
		}
		while (ptr < rsize &&  (Mem[ptr] == ' ' || Mem[ptr] == '\n' || Mem[ptr] == '\r')) {
			ptr++;
			if (ptr == Size && unfinished) Next();
		}
		return sb.ToString();
	}
}