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 Scala (2.11.7) (judge verdict: Accepted)

object Main {
  import java.io.{BufferedReader, InputStream, InputStreamReader}
  import java.util.StringTokenizer
  import scala.reflect.ClassTag

  def main(args: Array[String]): Unit = {
    val out = new java.io.PrintWriter(System.out)
    new Main(out, new InputReader(System.in)).solve()
    out.flush()
  }

  private[this] val oj = System.getenv("ATCODER_DEBUG") == null
  @inline private final def DEBUG(f: => Unit): Unit = {
    if (!oj){ f }
  }
  @inline private final def debug(as: Array[Boolean]): Unit = if (!oj){ debug(as.map(x => if(x) "1" else "0").mkString) }
  @inline private final def debug(as: Array[Int]): Unit = if (!oj){ debug(as.mkString(" ")) }
  @inline private final def debug(as: Array[Long]): Unit =if (!oj){ debug(as.mkString(" ")) }
  @inline private final def debugDim(m: Array[Array[Long]]): Unit = if (!oj){
    REP(m.length) { i =>
      debug(m(i))
    }
  }
  @inline private final def debugDimFlip(m: Array[Array[Long]]): Unit = if (!oj){
    REP(m(0).length) { j =>
      REP(m.length) { i =>
        System.err.print(m(i)(j))
        System.err.print(" ")
      }
      System.err.println()
    }
  }
  @inline private final def debug(s: => String): Unit = DEBUG {
    System.err.println(s)
  }
  @inline private final def debugL(num: => Long): Unit = DEBUG {
    System.err.println(num)
  }
  private def isDebug[A](debug: => A, online: => A): A = {
    if (oj) online else debug
  }

  class InputReader(val stream: InputStream) {
    private[this] val reader = new BufferedReader(new InputStreamReader(stream), 32768)
    private[this] var tokenizer: StringTokenizer = _

    @inline private[this] final def next(): String = {
      while (tokenizer == null || !tokenizer.hasMoreTokens)
        tokenizer = new StringTokenizer(reader.readLine)
      tokenizer.nextToken
    }

    def nextInt(): Int = Integer.parseInt(next())
    def nextLong(): Long = java.lang.Long.parseLong(next())
    def nextChar(): Char = next().charAt(0)

    def ni(): Int = nextInt()
    def nl(): Long = nextLong()
    def nc(): Char = nextChar()
    def ns(): String = next()
    def ns(n: Int): Array[Char] = ns().toCharArray
    def na(n: Int, offset: Int = 0): Array[Int] = map(n)(_ => ni() + offset)
    def na2(n: Int, offset: Int = 0): (Array[Int], Array[Int]) = {
      val A1, A2 = Array.ofDim[Int](n)
      REP(n) { i =>
        A1(i) = ni() + offset
        A2(i) = ni() + offset
      }
      (A1, A2)
    }
    def nm(n: Int, m: Int): Array[Array[Int]] = {
      val A = Array.ofDim[Int](n, m)
      REP(n) { i =>
        REP(m) { j =>
          A(i)(j) = ni()
        }
      }
      A
    }
    def nal(n: Int): Array[Long] = map(n)(_ => nl())
    def nm_c(n: Int, m: Int): Array[Array[Char]] = map(n) (_ => ns(m))
  }

  @inline private final def REP(n: Int, offset: Int = 0)(f: Int => Unit): Unit = {
    var i = offset
    val N = n + offset
    while(i < N) { f(i); i += 1 }
  }
  @inline private final def REP_r(n: Int, offset: Int = 0)(f: Int => Unit): Unit = {
    var i = n - 1 + offset
    while(i >= offset) { f(i); i -= 1 }
  }
  @inline private final def TO(from: Int, to: Int)(f: Int => Unit): Unit = {
    REP(to - from + 1, from)(f)
  }
  def map[@specialized A: ClassTag](n: Int, offset: Int = 0)(f: Int => A): Array[A] = {
    val res = Array.ofDim[A](n)
    REP(n)(i => res(i) = f(i + offset))
    res
  }

  def sumL(as: Array[Int]): Long = {
    var s = 0L
    REP(as.length)(i => s += as(i))
    s
  }
  def cumSum(as: Array[Int]): Array[Long] = {
    val cum = Array.ofDim[Long](as.length + 1)
    REP(as.length) { i =>
      cum(i + 1) = cum(i) + as(i)
    }
    cum
  }
}

object Workspace {
  case class Edge(to: Int, weight: Int, color: Int)
  case class Query(u: Int, v: Int)
  case class Queried(id: Int, color: Int, y: Int, co: Int)
}

class Main(out: java.io.PrintWriter, sc: Main.InputReader) {
  import sc._
  import Main._
  import java.util.Arrays.sort

  import scala.collection.mutable
  import math.{abs, max, min}
  import mutable.ArrayBuffer
  import Workspace._

  // toIntとか+7とかするならvalにしろ
  final private[this] val MOD = 1000000007

  /**
    * @return (depth, parent, queue)
    */
  def traceBfs(g: Array[ArrayBuffer[Edge]], rt: Int = 0): (Array[Int], Array[Int], Array[Int]) = {
    val n = g.length
    val INF = 1e9.toInt + 10
    val q, p = Array.ofDim[Int](n)
    q(0) = rt
    p(rt) = -1
    val d = Array.fill[Int](n)(INF)
    d(rt) = 0
    var cur = 0
    var last = 1
    while (cur < last) {
      val v = q(cur)
      REP(g(v).length) { i =>
        val u = g(v)(i).to
        if (d(u) == INF) {
          d(u) = d(v) + 1
          p(u) = v
          q(last) = u
          last += 1
        }
      }
      cur += 1
    }
    (d, p, q)
  }

  /**
    * Lowest Common Ancestor
    * @param g packで作ったグラフでおきかえること
    * @param K pow(2,K) >= N-1 なのか？ダブリングの深さ
    */
  class LCA(g: Array[ArrayBuffer[Edge]], N: Int, K: Int, rt: Int = 0){
    private[this] val ZERO = -1

    private[this] val (depth, parent, _) = traceBfs(g, rt)
    parent(rt) = rt // ルートを-1じゃなくてrtにする

    private[this] val anc = Array.ofDim[Int](K, N)
    REP(N) { i =>
      anc(0)(i) = parent(i)
    }
    REP(K - 1, 1) { k =>
      REP(N) { i =>
        anc(k)(i) = anc(k-1)(anc(k-1)(i))
      }
    }

    private def getParent(v: Int, k: Int, d: Int): Int = {
      if (d == 0) v
      else if (d % 2 == 1) getParent(anc(k)(v), k + 1, d / 2)
      else getParent(v, k + 1, d / 2)
    }

    /**
      * lca(v, ZERO) = v
      */
    def apply(v: Int, u: Int): Int = {
      def step(v0: Int, u0: Int, k: Int): Int = {
        if (k == -1) {
          anc(0)(v0) // １つ前の子を返したかったら(v0, u0)を返す
        } else if (anc(k)(v0) == anc(k)(u0)) {
          step(v0, u0, k - 1)
        } else {
          step(anc(k)(v0), anc(k)(u0), k - 1)
        }
      }

      if (v == ZERO) u
      else if (u == ZERO) v
      else {
        val d = min(depth(v), depth(u))
        val v0 = getParent(v, 0, depth(v) - d)
        val u0 = getParent(u, 0, depth(u) - d)
        if (v0 == u0) v0 // この時点で同じだったらダブリングで調べる必要がない
        else step(v0, u0, anc.length - 1)
      }
    }
  }

  def solve(): Unit = {
    val n, Q = ni()
    val g = Array.fill[ArrayBuffer[Edge]](n)(ArrayBuffer())
    REP(n - 1) { _ =>
      val a, b, color = ni() - 1
      val weight = ni()
      g(a) += Edge(b, weight, color)
      g(b) += Edge(a, weight, color)
    }

    val lca = new LCA(g, n, 17)

    val q = Array.ofDim[Query](Q)
    val queriedColor = Array.fill[ArrayBuffer[Queried]](n)(ArrayBuffer())
    REP(Q) { i =>
      val color = ni() - 1
      val y = ni()
      val u, v = ni() - 1
      q(i) = Query(u, v)
      queriedColor(u) += Queried(i, color, y, 1)
      queriedColor(v) += Queried(i, color, y, 1)
      queriedColor(lca(u, v)) += Queried(i, color, y, -2)
    }

    val dp = Array.ofDim[Int](n)
    val cnt = Array.ofDim[Int](n - 1)
    val sum = Array.ofDim[Int](n - 1)
    val queryDiff = Array.ofDim[Int](Q)
    def add(e: Edge): Unit = {
      cnt(e.color) += 1
      sum(e.color) += e.weight
    }

    def del(e: Edge): Unit = {
      cnt(e.color) -= 1
      sum(e.color) -= e.weight
    }

    def copyAns(v: Int): Unit = {
      val cs = queriedColor(v)
      REP(cs.length) { i =>
        val q = cs(i)
        val c = q.color
        queryDiff(q.id) += q.co * (cnt(c) * q.y - sum(c))
      }
    }

    def dfs(g: Array[ArrayBuffer[Edge]], rt: Int = 0) = {
      val n = g.length
      val stack = Array.ofDim[Edge](n)
      var s = 0
      val ix = Array.ofDim[Int](n)
      val parent = Array.ofDim[Int](n)
      parent(rt) = -1
      var p = 0
      p += 1
      stack(s) = Edge(rt, 0, -1) // 偽エッジ
      s += 1

      while(s > 0) {
        val v = stack(s-1).to
        val es = g(v)
        if (ix(v) == 0) copyAns(v)

        if (ix(v) == es.length) {
          s -= 1
          val back = stack(s)
          if (back.to != rt) del(back)
        }
        else {
          val e = es(ix(v))
          if (e.to != parent(v)) {
            add(e)
            dp(e.to) = dp(v) + e.weight
            parent(e.to) = v
            p += 1
            stack(s) = e
            s += 1
          }
          ix(v) += 1
        }
      }
    }

    dfs(g)
    REP(Q) { i =>
      val v = q(i).v
      val u = q(i).u
      val anc = lca(v, u)
      val base = dp(v) + dp(u) -dp(anc) * 2
      out.println(base + queryDiff(i))
    }
  }
}