#include <bits/stdc++.h>
template<typename T>
class fixed_point : T {
  public:
    explicit constexpr fixed_point (T&& t)
      : T(std::forward<T>(t)) {}
    template<typename... Args>
    constexpr decltype(auto) operator()(Args&&... args) const
      {return T::operator()(*this, std::forward<Args>(args)...);}
};
template<typename T>
static inline constexpr decltype(auto) fix (T&& t) {
  return fixed_point<T>{std::forward<T>(t)};
}
// Dependent libraries: Combinator.
template <typename T>
class heavy_light_decomposition {
    struct edge {size_t to; T cost;};
    bool built; size_t n;
    std::vector<std::vector<edge>> graph;
    std::vector<size_t>             prt, sub, vid, head;
    std::vector<unsigned>           unweighted_depth;
    std::vector<T>                 weighted_depth;

  public:
    using edge_type = edge;
    using cost_type = T;
    heavy_light_decomposition(size_t n) :
      built(false), n(n),
      graph(n), prt(n), sub(n, 1), vid(n), head(n),
      unweighted_depth(n, 0), weighted_depth(n, 0) {}
    
    // Get the vertex id.
    auto id (size_t u) const -> size_t {
      return vid.at(u);}

    // Insert an edge.
    void insert (size_t u, size_t v, T cost = 1) {
        assert(!built);
        graph.at(u).push_back({v, cost});
        graph.at(v).push_back({u, cost});
      }
    // Deflag the built flag.
    void deflag () {
        assert(built);
        built = false;
      }
    // Build an HL Decomposition.
    void build (size_t root = 0) {
        assert(!built); built = true;
        fix ([&](auto dfs, size_t crr, size_t p) -> void {
          prt.at(crr) = p;
          for (auto& e : graph.at(crr)) {
            if (e.to == p) continue;
            unweighted_depth.at(e.to) = unweighted_depth.at(crr) + 1;
            weighted_depth.at(e.to)   = weighted_depth.at(crr) + e.cost;
            dfs(e.to, crr);
            sub.at(crr) += sub.at(e.to);
            if (sub.at(e.to) > sub.at(graph.at(crr).front().to))
              std::swap(e, graph.at(crr).front());
          }
        })(root, root);
        size_t now = 0; head.at(root) = root;
        fix ([&](auto dfs, size_t crr, size_t p) -> void {
          vid.at(crr) = now, now++;
          for (auto const& e : graph.at(crr)) {
            if (e.to == p) continue;
            head.at(e.to) = (e.to == graph.at(crr).front().to
              ? head.at(crr) : e.to);
            dfs(e.to, crr);
          }
        })(root, root);
      }
    // Call a function f([u, v]),
    // where u and v is the original vertex id,
    // while the parameter of f is by the HLD-order. 
    template <typename F>
    auto for_each_edge (size_t u, size_t v, F f) const
      -> void
      {
        assert(built);
        while (head.at(u) != head.at(v)) {
          if (vid.at(u) > vid.at(v)) std::swap(u, v);
          f(vid.at(head.at(v)), vid.at(v));
          v = prt.at(head.at(v));
        }
        if (vid.at(u) > vid.at(v)) std::swap(u, v);
        if (u != v) f(vid.at(u) + 1, vid.at(v));
      }
    // Calculate the lowest common ancestor.
    auto lca (size_t u, size_t v) const -> size_t {
        assert(built);
        while (head.at(u) != head.at(v)) {
          if (vid.at(u) > vid.at(v)) std::swap(u, v);
          v = prt.at(head.at(v));
        }
        return vid.at(u) < vid.at(v) ? u : v;
      }
    // Calculate the count-based distance between u and v.
    auto unweighted_dist (size_t u, size_t v) const -> unsigned {
        auto& x = unweighted_depth;
        return x.at(u) + x.at(v) - 2 * x.at(lca(u, v));
      }
    // Calculate the cost-based distance between u and v.
    auto weighted_dist (size_t u, size_t v) const -> unsigned {
        auto& x = weighted_depth;
        return x.at(u) + x.at(v) - 2 * x.at(lca(u, v));
      }
};
template<typename T, typename F, std::enable_if_t<
  std::is_same<std::result_of_t<F(T, T)>, T>::value,
  std::nullptr_t
> = nullptr>
class segment_tree {
    using vec = std::vector<T>;
    size_t ht, n, N;
    F o; T id; vec tab;
    void cal (size_t u) {tab.at(u) = o(tab.at(2 * u), tab.at(2 * u + 1));}

  public:
    using value_type    = T;
    using function_type = F;
    segment_tree(
        size_t size,
        F      merge_function,
        T      id,
        vec    v
      ) :
      ht(std::log2(size) + 2), n(std::pow(2, ht - 1)), N(n * 2),
      o(merge_function), id (id), tab(N)
      {
        assert(n > 0);
        v.resize(n, id);
        std::copy(v.begin(), v.end(), tab.begin() + n);
        for (size_t i = n - 1; i < n; i--) cal(i);
      }
    segment_tree(
        size_t size,
        F      merge_function,
        T      id
      ) :
      segment_tree(size, std::move(merge_function), id, vec(size, id)){}

    // Change a value at the leaf.
    void update (size_t u, T val) {
      tab.at(u += n) = val;
      for (u /= 2; u != 0; u /= 2) cal(u);
    }
    // Range-something query at [l, r[.
    auto query (size_t l, size_t r) const -> T {
      struct state {size_t top, left, right;};
      auto ret = id;
      std::stack<state> stk; stk.push({1, 0, n});
      while (!stk.empty()) {
        auto now = stk.top(); stk.pop();
        if (l <= now.left && now.right <= r) {
          ret = o(ret, tab.at(now.top));
          continue;
        }
        size_t mid = (now.left + now.right) / 2;
        if (l < mid) stk.push({2 * now.top,     now.left,  mid});
        if (mid < r) stk.push({2 * now.top + 1, mid, now.right});
      }
      return ret;
    }
};
template<typename T, typename F, typename... Args>
auto make_segment_tree (
    size_t  size,
    F       merge_function,
    Args... args
  )
  {
    return segment_tree<T, F>(
      size, std::move(merge_function), std::forward<Args>(args)...);
  }
int main() {
  std::cin.tie(0); std::cin.sync_with_stdio(false);
  size_t n, q; std::cin >> n >> q;
  auto hld = heavy_light_decomposition<int>(n);
  struct edge_t {size_t a, b; int d;};
  auto edges_by_color = std::vector<std::vector<edge_t>>(n - 1);
  for (size_t i = 0; i < n - 1; i++) {
    size_t a, b; int c, d;
    std::cin >> a >> b >> c >> d;
    a--, b--, c--;
    hld.insert(a, b, d);
    edges_by_color.at(c).push_back({a, b, d});
  }
  int root = 0; hld.build(root);
  struct query_t {size_t id; int y; size_t u, v;};
  auto queries_by_color = std::vector<std::vector<query_t>>(n - 1);
  std::vector<int> ans(q);
  for (size_t i = 0; i < q; i++) {
    int x, y; size_t u, v;
    std::cin >> x >> y >> u >> v;
    u--, v--, x--;
    ans.at(i) = hld.weighted_dist(u, v);
    queries_by_color.at(x).push_back({i, y, u, v});
  }
  auto cnt = make_segment_tree<int>(n, std::plus<int>{}, 0);
  auto dst = make_segment_tree<int>(n, std::plus<int>{}, 0);
  for (size_t col = 0; col < n - 1; col++) {
    for (auto const& e : edges_by_color.at(col)) {
      cnt.update(std::max(hld.id(e.a), hld.id(e.b)), 1);
      dst.update(std::max(hld.id(e.a), hld.id(e.b)), e.d);
    }
    for (auto const& q : queries_by_color.at(col)) {
      hld.for_each_edge(q.u, q.v, [&](size_t l, size_t r){
          ans.at(q.id) += q.y * cnt.query(l, r + 1);
          ans.at(q.id) -= dst.query(l, r + 1);
        });
    }
    for (auto const& e : edges_by_color.at(col)) {
      cnt.update(std::max(hld.id(e.a), hld.id(e.b)), 0);
      dst.update(std::max(hld.id(e.a), hld.id(e.b)), 0);
    }
  }
  std::for_each(ans.begin(), ans.end(), [](auto&& x){
    std::cout << x << std::endl;});
  return 0;
}

Measured memory_bytes: 32688