#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
#define REP(i,n) for(int i=0,_n=(int)(n);i<_n;++i)
#define ALL(v) (v).begin(),(v).end()
#define CLR(t,v) memset(t,(v),sizeof(t))
template<class T1,class T2>ostream& operator<<(ostream& os,const pair<T1,T2>&a){return os<<"("<<a.first<<","<<a.second<< ")";}
template<class T>void pv(T a,T b){for(T i=a;i!=b;++i)cout<<(*i)<<"\t";cout<<endl;}
template<class T>void chmin(T&a,const T&b){if(a>b)a=b;}
template<class T>void chmax(T&a,const T&b){if(a<b)a=b;}


int nextInt() { int x; scanf("%d", &x); return x;}
ll nextLong() { ll x; scanf("%lld", &x); return x;}



const int MAX_N = 100000 + 10;
const int SZ = MAX_N * 2 + 10;

struct E{
  int to;
  int color;
  int dist;
};
vector<E> g[MAX_N];

/**
* Sparse Table による RMQ の実装。
* 区間のmin
* 構築O(nlogn), クエリO(1)
* 更新ができない。
* SegmentTreeによる実装よりも定数倍がかなり高速。
*/
struct RMQStatic {
  int *a;
  int dp_min[20][SZ]; // [i][j] := answer index of start_pos == j, len == 2^i

  int log2(int b) {return 31 - __builtin_clz(b);}

  // O(NlogN)
  void build(int* a, int N) {
    this->a = a;
    int M = log2(N) + 1;
    REP(i, N) dp_min[0][i] = i;
    for (int i = 1, len = 1; i < M; i++, len <<= 1) {
      for (int j = 0; j + len < N; j++) {
        {
          int s = dp_min[i - 1][j];
          int t = dp_min[i - 1][j + len];
          dp_min[i][j] = a[s] <= a[t] ? s : t;
        }
      }
    }
  }

  // O(1)
  // [from..to]の間で最小の値の添字を返す。複数ある場合は一番最初のを返す
  int query(int from, int to) {
    int k = log2(to - from + 1);
    int s = dp_min[k][from];
    int t = dp_min[k][to - (1 << k) + 1];
    return a[s] <= a[t] ? s : t;
  }
};

int nod[SZ]; // dfsして i 番目に訪問した頂点の番号
int dep[SZ]; // dfsして i 番目に訪問した頂点の深さ
int dist[SZ]; // root からの距離
int color[SZ];
int weight[SZ];
int apr[MAX_N]; // nod において頂点 v が始めに出現する添字

void dfs(int v, int prev, int d, int dis, int& sp) {
  if (apr[v] == -1)
    apr[v] = sp;
  nod[sp] = v;
  dep[sp] = d;
  dist[sp] = dis;
  sp++;
  for (auto e : g[v]) if (e.to != prev) {
    color[sp] = e.color;
    weight[sp] = e.dist;
    dfs(e.to, v, d + 1, dis + e.dist, sp);
    nod[sp] = v; dep[sp] = d; dist[sp] = dis; color[sp] = -e.color; weight[sp] = e.dist; sp++;
  }
}

RMQStatic rmq;

struct Query {
  int idx;
  int a;
  int b;
  int x;
  int y;
  int ansc;
  int answ;
};


int main2() {

  REP(i, MAX_N) g[i].clear();

  int N = nextInt();
  int Q = nextInt();

  REP(i, N-1) {
    int a = nextInt() - 1;
    int b = nextInt() - 1;
    int c = nextInt();
    int d = nextInt();
    g[a].push_back({b, c, d});
    g[b].push_back({a, c, d});
  }

  int sp = 0;
  CLR(apr, -1);
  dfs(0, -1, 0, 0, sp);
  rmq.build(dep, sp);

  // cout << "nod  : "; pv(nod, nod + sp);
  // cout << "dep  : "; pv(dep, dep + sp);
  // cout << "dist : "; pv(dist, dist + sp);
  // cout << "color: "; pv(color, color + sp);
  // cout << "weig : "; pv(weight, weight + sp);
  // cout << "apr  : "; pv(apr, apr + sp);

  vector<Query> qs;
  REP(q, Q) {
    int x = nextInt();
    int y = nextInt();
    int u = nextInt() - 1;
    int v = nextInt() - 1;

    int ui = min(apr[u], apr[v]);
    int vi = max(apr[u], apr[v]);
    int m = rmq.query(ui, vi);
    // int orgDist = (dist[ui] - dist[m]) + (dist[vi] - dist[m]);
    // cout << "q: " << x << " " << y << " " << u << " " << v << " -> " << ui << " -- " << m << " -- " << vi << " d=" << orgDist << endl;
    qs.push_back({q*2 + 0, ui+1, m+1, x, y, -1, -1});
    qs.push_back({q*2 + 1, m+1, vi+1, x, y, -1, -1});
  }

  const int R = (int)(sqrt(N + 0.5) + 1);
  sort(ALL(qs), [&](const Query&o1, const Query&o2){
    if (o1.a / R != o2.a / R) return o1.a / R < o2.a / R;
    return o1.b < o2.b;
  });

  vector<int> count(SZ);
  vector<int> ws(SZ);
  auto add = [&](const int color, const int w) {
    if (color > 0) { count[color]++; ws[color] += w; }
    if (color < 0) { count[-color]--; ws[-color] -= w; }
  };
  int l = 0, r = 0; // [l, r)
  REP(i, qs.size()) {
    Query q = qs[i];
    while (r < q.b) { add( color[r], weight[r]); r++; }
    while (r-1 >= q.b) { add(-color[r-1], weight[r-1]); r--; }
    while (l < q.a) { add(-color[l], weight[l]); l++; }
    while (l-1 >= q.a) { add( color[l-1], weight[l-1]); l--; }
    qs[i].ansc = abs(count[q.x]);
    qs[i].answ = abs(ws[q.x]);
    // cout << "[" << q.a << "," << q.b << ") x=" << q.x << " => " << qs[i].ansc << "," << qs[i].answ << "   " << endl;
    // cout << "[" << l << "," << r << ")" << endl;
    // cout << "debug1: "; for (int i = l; i < r; i++) cout << color[i] << " "; cout << endl;
    // cout << "debug2: "; for (int i = l; i < r; i++) cout << weight[i] << " "; cout << endl;
    // cout << endl;
  }

  sort(ALL(qs), [&](const Query&o1, const Query&o2){
    return o1.idx < o2.idx;
  });
  for (int q = 0; q < Q; q++) {
    Query q0 = qs[q * 2 + 0];
    Query q1 = qs[q * 2 + 1];
    int ui = q0.a - 1;
    int m = q1.a - 1;
    int vi = q1.b - 1;
    int orgDist = (dist[ui] - dist[m]) + (dist[vi] - dist[m]);
    int c = q0.ansc + q1.ansc;
    int w = q0.answ + q1.answ;
    int ans = orgDist - w + c * q1.y;
    // cout << q1.x << "->" << q1.y << " " << q0.a << "----" << q0.b << "----" << q1.b << " " << c << " " << w  << " orgDist=" << orgDist << endl;
    printf("%d\n", ans);
  }
  return 0;
}

int main() {

#ifdef LOCAL
  for (;!cin.eof();cin>>ws)
#endif
    main2();
  return 0;
}

Measured memory_bytes: 41068