#include <bits/stdc++.h>
#define GET_REP(_1, _2, _3, NAME, ...) NAME
#define rep(...) GET_REP(__VA_ARGS__, irep, _rep)(__VA_ARGS__)
#define rep1(...) GET_REP()(__VA_ARGS__, irep1, _rep1)(__VA_ARGS__)
#define _rep(i, n) irep (i, 0, n)
#define _rep1(i, n) irep1(i, 1, n)
#define irep(i, a, n) for (int i = a; i < (int)(n); ++i)
#define irep1(i, a, n) for (int i = a; i <= (int)(n); ++i)
#define rrep(i, n) for (int i = (int)(n) - 1; i >= 0; --i)
#define rrep1(i, n) for (int i = (int)(n); i >= 1; --i)
#define allrep(X, x) for (auto &&X : x)
#define all(x) (x).begin(), (x).end()
#ifdef LOCAL
  #include "../../Lib/cout_container.hpp"
  #define debug(x) cerr << #x " => " << x << endl
#else
  #define debug(x) 0
#endif
using lint = long long;
constexpr int    INF  = 1 << 30;
constexpr lint   INFL = 1LL << 62;
constexpr int    MOD  = (int)1e9 + 7;
constexpr double EPS  = 1e-9;
using namespace std;
namespace { struct INIT { INIT() { cin.tie(0); ios::sync_with_stdio(false); cout << fixed << setprecision(15); } } INIT; }

using Weight = pair<int, int>;
struct Edge {
  int src, dst;
  Weight weight;
  Edge(void) {}
  Edge(int src, int dst) : src(src), dst(dst) {}
  Edge(int src, int dst, Weight weight) : src(src), dst(dst), weight(weight) {}
};
using Edges = vector<Edge>;
using Graph = vector<Edges>;

void edges_to_graph(Graph &graph, const Edges &edges, const int vertices, const bool directed = true) {
  graph.resize(vertices);
  for (const auto &e : edges) {
    graph[e.src].push_back(e);
    if (!directed) graph[e.dst].emplace_back(Edge(e.dst, e.src, e.weight));
  }
}

vector<unordered_set<int>> need;
// <辺の色, <その色の辺の数, その色の辺の長さの和>>
unordered_map<int, pair<int, lint>> col;
vector<unordered_map<int, pair<int, lint>>> colmap;
class EulerTour {
  int num = 0;
  void et_dfs(const Graph &graph, const int v, const int p, const int dep) {
    tour.push_back(v);
    in[v] = num++;
    depth_tour.push_back(dep);
    depth[v] = dep;
    for (const auto &e : graph[v]) {
      if (e.dst == p) continue;
      len[e.dst] = len[v] + e.weight.second;
      et_dfs(graph, e.dst, v, dep + 1);
      tour.push_back(v);
      depth_tour.push_back(dep);
      ++num;
    }
    out[v] = num - 1;
  }
  void et_dfs2(const Graph &graph, const int v, const int p) {
    allrep (n, need[v]) colmap[v][n] = col[n];
    for (const auto &e : graph[v]) {
      if (e.dst == p) continue;
      ++col[e.weight.first].first;
      col[e.weight.first].second += e.weight.second;
      et_dfs2(graph, e.dst, v);
      --col[e.weight.first].first;
      col[e.weight.first].second -= e.weight.second;
    }
  }

  public:
  void dfs(const Graph &graph, const int v) { et_dfs2(graph, v, -1); }
  vector<int>  tour, depth_tour, in, out, depth;
  vector<lint> len;
  EulerTour(void) {}
  EulerTour(const Graph &graph, const int v) { init(graph, v); }
  void init(const Graph &graph, const int v) {
    tour.clear();
    depth_tour.clear();
    in.assign(graph.size(), 0);
    out.assign(graph.size(), 0);
    depth.assign(graph.size(), 0);
    len.resize(graph.size());
    colmap.resize(graph.size());
    et_dfs(graph, v, -1, 0);
  }
};

template <typename T>
class SparseTable {
  vector<vector<int>> st;
  vector<int> lookup;
  vector<T>   vec;

  public:
  SparseTable(void) {}
  SparseTable(const vector<T> &original_vecter) { init(original_vecter); }
  void init(const vector<T> &original_vecter) {
    vec = original_vecter;
    int lgsize = 0, vsize = vec.size();
    while ((1 << lgsize) <= vsize) ++lgsize;
    st.assign((1 << lgsize), vector<int>(lgsize));
    for (int i = 0; i < vsize; ++i) st[i][0] = i;
    for (int j = 1; j < lgsize; ++j) {
      for (int i = 0; i + (1 << j) <= (1 << lgsize); ++i) {
        int l = st[i][j - 1], r = st[i + (1 << (j - 1))][j - 1];
        st[i][j] = vec[l] <= vec[r] ? l : r;
      }
    }
    lookup.assign(vsize + 1, 0);
    for (int i = 2; i <= vsize; ++i) lookup[i] = lookup[i >> 1] + 1;
  }
  T query(int l, int r) {
    ++r;
    int lgsize = lookup[r - l];
    return min(vec[st[l][lgsize]], vec[st[r - (1 << lgsize)][lgsize]]);
  }
  int query_index(int l, int r) {
    ++r;
    int lgsize = lookup[r - l];
    T   lv = vec[st[l][lgsize]], rv = vec[st[r - (1 << lgsize)][lgsize]];
    if (lv < rv) return st[l][lgsize];
    else if (lv > rv) return st[r - (1 << lgsize)][lgsize];
    else return min(st[l][lgsize], st[r - (1 << lgsize)][lgsize]);
  }
};

class LowestCommonAncestor {
  class PM1RangeMinimumQuery {
    int block_size, block_num;
    vector<int> vec, block_bit;
    vector<vector<vector<int>>> lookup;
    SparseTable<pair<int, int>> st;

    public:
    PM1RangeMinimumQuery(void) {}
    void init(const vector<int> &original_vecter) {
      vec = original_vecter;
      int vsize = vec.size();
      block_size = 0;
      while ((1 << block_size) < vsize) ++block_size;
      block_size = max(1, (block_size >> 1));
      while (vsize % block_size) {
        vec.push_back(vec.back() + 1);
        ++vsize;
      }
      block_num = vsize / block_size;
      vector<pair<int, int>> block_min(block_num, {INT_MAX, INT_MAX});
      for (int i = 0; i < vsize; ++i) block_min[i / block_size] = min(block_min[i / block_size], {vec[i], i});
      st.init(block_min);
      block_bit.assign(block_num, 0);
      for (int i = 0; i < block_num; ++i) {
        int left = block_size * i;
        for (int j = 0; j < block_size - 1; ++j) {
          if (vec[left + j] < vec[left + j + 1]) block_bit[i] |= (1 << j);
        }
      }
      int bit_all = 1 << (block_size - 1);
      lookup.assign(bit_all, vector<vector<int>>(block_size, vector<int>(block_size)));
      for (int b = 0; b < bit_all; ++b) {
        for (int i = 0; i < block_size; ++i) {
          int mini = 0, now = 0;
          for (int j = i + 1; j < block_size; ++j) {
            b&(1 << (j - 1)) ? ++now : --now;
            if (mini > now) {
              mini = now;
              lookup[b][i][j] = j - i;
            } else {
              lookup[b][i][j] = lookup[b][i][j - 1];
            }
          }
        }
      }
    }
    int query_index(int l, int r) {
      int lb = l / block_size, rb = r / block_size;
      int lbpos = l % block_size, rbpos = r % block_size;
      if (lb == rb) return l + lookup[block_bit[lb]][lbpos][rbpos];
      int lbmini = l, rbmini = r;
      if (lbpos != 0) {
        lbmini = l + lookup[block_bit[lb]][lbpos][block_size - 1];
        ++lb;
      }
      if (rbpos != block_size - 1) {
        rbmini = r - rbpos + lookup[block_bit[rb]][0][rbpos];
        --rb;
      }
      int mini = vec[lbmini] < vec[rbmini] ? lbmini : rbmini;
      if (lb <= rb) {
        pair<int, int> stpair = st.query(lb, rb);
        if (stpair.first < vec[mini]) mini = stpair.second;
      }
      return mini;
    }
  };
  PM1RangeMinimumQuery rmq;

  public:
  EulerTour et;
  LowestCommonAncestor(void) {}
  LowestCommonAncestor(const Graph &graph, const int v) { init(graph, v); }
  void init(const Graph &graph, const int v) {
    et.init(graph, v);
    rmq.init(et.depth_tour);
  }
  int query(const int a, const int b) {
    int l = et.in[a], r = et.in[b];
    if (l > r) swap(l, r);
    int idx = rmq.query_index(l, r);
    return et.tour[idx];
  }
};

Edges edges;
Graph graph;

int main(void) {
  int n, q;
  cin >> n >> q;
  edges.resize(n - 1);
  rep (i, n - 1) {
    int col, len;
    cin >> edges[i].src >> edges[i].dst >> col >> len;
    --edges[i].src;
    --edges[i].dst;
    edges[i].weight = {col, len};
  }
  need.resize(n);
  vector<int> x(q), y(q), u(q), v(q), z(q);
  rep (i, q) {
    cin >> x[i] >> y[i] >> u[i] >> v[i];
    --u[i];
    --v[i];
  }
  edges_to_graph(graph, edges, n, false);
  LowestCommonAncestor lca(graph, 0);
  rep (i, q) {
    z[i] = lca.query(u[i], v[i]);
    need[u[i]].insert(x[i]);
    need[v[i]].insert(x[i]);
    need[z[i]].insert(x[i]);
  }
  lca.et.dfs(graph, 0);
  vector<lint> ans(q);
  EulerTour    &ett = lca.et;
  rep (i, q) {
    int z = lca.query(u[i], v[i]);
    ans[i]  = ett.len[u[i]] + ett.len[v[i]] - 2 * ett.len[z];
    ans[i] -= colmap[u[i]][x[i]].second + colmap[v[i]][x[i]].second - 2 * colmap[z][x[i]].second;
    ans[i] += y[i] * (colmap[u[i]][x[i]].first + colmap[v[i]][x[i]].first - 2 * colmap[z][x[i]].first);
  }
  rep (i, q) cout << ans[i] << "\n";
  return 0;
}

Measured memory_bytes: 63376