#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>
#include <tuple>
#include <utility>
#include <functional>
#include <stack>
using namespace std;

template <typename MonoidType, typename OperatorType>
struct LazySegmentTree {
    using MMtoM = function< MonoidType(MonoidType, MonoidType) >;
    using OOtoO = function< OperatorType(OperatorType, OperatorType) >;
    using MOtoM = function< MonoidType(MonoidType, OperatorType) >;
    using OItoO = function< OperatorType(OperatorType, int) >;

    // node, lazy, update flag (for lazy), identity element
    int n;
    vector<MonoidType> node;
    vector<OperatorType> lazy;
    vector<bool> need_update;
    MonoidType E0;
    OperatorType E1;

    // update / combine / lazy / accumulate function
    OOtoO upd_f;
    MMtoM cmb_f;
    MOtoM lzy_f;
    OItoO acc_f;

    void build(int m, vector<MonoidType> v = vector<MonoidType>()) {
        if(v != vector<MonoidType>()) m = v.size();
        n = 1; while(n < m) n *= 2;

        node = vector<MonoidType>(2*n-1, E0);
        lazy = vector<OperatorType>(2*n-1, E1);
        need_update = vector<bool>(2*n-1, false);
        if(v != vector<MonoidType>()) {
            for(int i=0; i<m; i++) {
                node[n-1+i] = v[i];
            }
            for(int i=n-2; i>=0; i--) {
                node[i] = cmb_f(node[2*i+1], node[2*i+2]);
            }
        }
    }

    // initialize
    LazySegmentTree() {}
    LazySegmentTree(int n_, MonoidType E0_, OperatorType E1_,
                    OOtoO upd_f_, MMtoM cmb_f_, MOtoM lzy_f_, OItoO acc_f_,
                    vector<MonoidType> v = vector<MonoidType>()) :
        E0(E0_), E1(E1_),
        upd_f(upd_f_), cmb_f(cmb_f_), lzy_f(lzy_f_), acc_f(acc_f_) {
        build(n_, v);
    }

    void eval(int k, int l, int r) {
        if(!need_update[k]) return;
        node[k] = upd_f(node[k], acc_f(lazy[k], r - l));
        if(r - l > 1) {
            lazy[2*k+1] = lzy_f(lazy[2*k+1], lazy[k]);
            lazy[2*k+2] = lzy_f(lazy[2*k+2], lazy[k]);
            need_update[2*k+1] = need_update[2*k+2] = true;
        }
        lazy[k] = E1;
        need_update[k] = false;
    }

    void update(int a, int b, OperatorType x, int l, int r, int k) {
        eval(k, l, r);
        if(b <= l or  r <= a) return;
        if(a <= l and r <= b) {
            lazy[k] = lzy_f(lazy[k], x);
            need_update[k] = true;
            eval(k, l, r);
        }
        else {
            int mid = (l + r) / 2;
            update(a, b, x, l, mid, 2*k+1);
            update(a, b, x, mid, r, 2*k+2);
            node[k] = cmb_f(node[2*k+1], node[2*k+2]);
        }
    }

    MonoidType query(int a, int b, int l, int r, int k) {
        if(b <= l or  r <= a) return E0;
        eval(k, l, r);
        if(a <= l and r <= b) return node[k];
        int mid = (l + r) / 2;
        MonoidType vl = query(a, b, l, mid, 2*k+1);
        MonoidType vr = query(a, b, mid, r, 2*k+2);
        return cmb_f(vl, vr);
    }

    // update [a, b)-th element (applied value, x)
    void update(int a, int b, OperatorType x) {
        update(a, b, x, 0, n, 0);
    }

    // range query for [a, b)
    MonoidType query(int a, int b) {
        return query(a, b, 0, n, 0);
    }

    void dump() {
        fprintf(stderr, "[lazy]\n");
        for(int i=0; i<2*n-1; i++) {
            if(i == n-1) fprintf(stderr, "xxx ");
            if(lazy[i] == E1) fprintf(stderr, "  E ");
            else fprintf(stderr, "%3d ", lazy[i]);
        }
        fprintf(stderr, "\n");

        fprintf(stderr, "[node]\n");
        for(int i=0; i<2*n-1; i++) {
            if(i == n-1) fprintf(stderr, "xxx ");
            if(node[i] == E0) fprintf(stderr, "  E ");
            else fprintf(stderr, "%3d ", node[i]);
        }
        fprintf(stderr, "\n");
    }
};

// HL 分解
// ある頂点 v の子たち c_i について、それを根とする部分木の頂点数が
// 最大のものの 1 つを c_h とおく。このとき、辺 (v, c_h) は "heavy" な辺であるという。
// それ以外の辺は "light" な辺であるという。

// すべての辺を "heavy" と "light" に分類すると、木は "heavy" からなるパス
// (下のコードだと chain) に分解 ("heavy" でつながっている頂点をひとつにまとめる) できる。
// これを HL 分解という。

// chain を縮約したあとの木の高さは O(log N) である。
// これは、"heavy" な辺の定義より、頂点 v と "light" な辺で結ばれている v の子それぞれに対して、
// 部分木の大きさが subsize(v) / 2 以下となっていることから導ける。
// "light" な辺を通るたびに部分木のサイズが半分以下になるため、深さは O(log N) となる。

// 移動元と行先と辺のコストを記録する構造体
template <typename T>
struct Edge {
    int from, to;
    T cost;
    Edge(int s, T d) : to(s), cost(d) {}
    Edge(int f, int s, T d) : from(f), to(s), cost(d) {}

    bool operator<(const Edge &e) const {
        return cost < e.cost;
    }
    bool operator>(const Edge &e) const {
        return cost > e.cost;
    }
};

template <typename T>
using Graph = vector< vector< Edge<T> > >;

template <typename MonoidType, typename OperatorType>
struct HLD {
    int N;
    const Graph<int> G;
    // ・元の木について
    // 根からの深さ、自分の親、自分を根にしたときの部分木のサイズ
    // および、その頂点から出る "heavy" な辺
    vector<int> depth, parent, subsize, heavy;
    // ・chain について
    // chain の先頭要素、末尾要素、その頂点の 1 つ次・前の要素、chain 内でのインデックス
    vector<int> head, last, prev, next, chain, idx;
    // chain の情報 (同じ vector 内にある要素どうしは同じ chain に属する)
    vector< vector<int> > chains;
    // 各 chain に載せる遅延評価セグ木 (抽象)
    vector< LazySegmentTree<MonoidType, OperatorType> > segs;

    HLD(const Graph<int> &H, int r=0) :
        N(H.size()), G(H), depth(N, -1), parent(N, 0), subsize(N, 0), heavy(N, -1),
        head(N), last(N), prev(N, -1), next(N, -1), chain(N, -1), idx(N, 0)
    {
        if(r != -1) decompose(r);
    }

    // root を根として分解
    void decompose(const int root) {
        stack<int> st; st.push(root);
        parent[root] = -1;
        depth[root] = 0;        

        while(st.size()) {
            int cur_v = st.top(); st.pop();
            // その頂点を初めて訪れた
            if(cur_v >= 0) {
                st.push(~cur_v);
                for(auto e : G[cur_v]) {
                    // 親の方向でなければ値を更新
                    if(depth[e.to] != -1) continue;
                    depth[e.to] = depth[cur_v] + 1;
                    parent[e.to] = cur_v;
                    st.push(e.to);
                }
            }
            // 帰りがけ
            else {
                int ma = 0;
                cur_v = ~cur_v;
                subsize[cur_v] = 1;
                for(auto e : G[cur_v]) {
                    if(parent[cur_v] == e.to) continue;
                    subsize[cur_v] += subsize[e.to];
                    if(ma < subsize[e.to]) {
                        // cur_v と部分木のサイズが最大の子を結ぶ
                        // (これが "heavy" の辺)
                        ma = subsize[e.to];
                        heavy[cur_v] = e.to;
                    }
                }
            }
        }

        st.push(root);
        while(st.size()) {
            int cur_v = st.top(); st.pop();
            for(auto e : G[cur_v]) {
                if(parent[cur_v] != e.to) st.push(e.to);
            }

            // すでにその頂点について構築済みなら、何もしない
            // (chain の先頭の頂点でない)
            if(chain[cur_v] != -1) continue;
            chains.push_back(vector<int>());
            vector<int> &path = chains.back();

            // cur_v を始点として、heavy な辺をたどりながら下がる
            for(int v=cur_v; v!=-1; v=heavy[v]) {
                path.push_back(v);
            }

            for(size_t i=0; i<path.size(); i++) {
                // path (chain) の i 番目の頂点 v に関する情報
                int v = path[i];
                head[v] = path.front(), last[v] = path.back();
                prev[v] = (i != 0 ? path[i-1] : -1);
                next[v] = (i+1 != path.size() ? path[i+1] : -1);
                chain[v] = (int)chains.size() - 1;
                idx[v] = i;
            }
        }
    }

    // chain の個数分だけ segtree を作る
    using MMtoM = function< MonoidType(MonoidType, MonoidType) >;
    using OOtoO = function< OperatorType(OperatorType, OperatorType) >;
    using MOtoM = function< MonoidType(MonoidType, OperatorType) >;
    using OItoO = function< OperatorType(OperatorType, int) >;
    MonoidType E0; OperatorType E1;
    OOtoO upd_f;
    MMtoM cmb_f;
    MOtoM lzy_f;
    OItoO acc_f;

    void buildSegtree(MonoidType E0_, OperatorType E1_,
                      OOtoO upd_f_, MMtoM cmb_f_, MOtoM lzy_f_, OItoO acc_f_,
                      vector<MonoidType> v = vector<MonoidType>()) {
        E0 = E0_, E1 = E1_;
        upd_f = upd_f_;
        cmb_f = cmb_f_;
        lzy_f = lzy_f_;
        acc_f = acc_f_;
        
        segs.clear();
        for(size_t i=0; i<chains.size(); i++) {
            segs.push_back(LazySegmentTree<MonoidType, OperatorType>(chains[i].size(), E0, E1,
                                                                     upd_f, cmb_f, lzy_f, acc_f, v));
        }
    }

    // 頂点 v について、どの chain に属するか、
    // またその chain の中でのインデックスは何かを pair で返す
    pair<int, int> get_index(int v) {
        return make_pair(chain[v], idx[v]);
    }

    void debug_path() {
        cerr << "size: " << chains.size() << endl;
        for(size_t i=0; i<chains.size(); i++) {
            fprintf(stderr, "path:");
            for(size_t k=0; k<chains[i].size(); k++) {
                fprintf(stderr, " %lld", chains[i][k]);
            }
            fprintf(stderr, "\n");
        }
    }

    // v が属する chain の先頭を返す (head[v] が根なら -1 を返す)
    int climb(int v) {
        return parent[ head[v] ];
    }

    // 頂点 u と v の最小共通祖先
    int lca(int u, int v) {
        // 同じ chain に属するまで登る
        while(chain[u] != chain[v]) {
            if(depth[head[u]] < depth[head[v]]) v = climb(v);
            else u = climb(u);
        }
        // 属する chain が同じになれば、浅いほうが LCA
        return depth[u] < depth[v] ? u : v;
    }

    // lca で分解されたあとのパスについて、クエリを処理
    void update_base(int u, int v, OperatorType x, int f) {
        while(1) {
            // u の方が深い
            if(depth[u] < depth[v]) swap(u, v);

            // 属する chain が異なる → 先頭まで全部更新
            if(chain[u] != chain[v]) {
                int lhs = 0, rhs = idx[u] + 1;
                segs[ chain[u] ].update(lhs, rhs, x);
                u = climb(u);
            }
            // 同じ → v を含めるかどうかに気をつけながら更新
            else {
                int lhs = idx[v] + 1 - f, rhs = idx[u] + 1;
                segs[ chain[u] ].update(lhs, rhs, x);
                break;
            }
        }
    }

    // 頂点 u - v 間の「点」それぞれについて更新
    // f は anc を更新対象に含めるかどうか
    void upd_vertex(int u, int v, OperatorType x) {
        int anc = lca(u, v);
        update_base(u, anc, x, 1);
        update_base(v, anc, x, 0);
    }

    // 頂点 u - v 間の「辺」それぞれについて更新
    void upd_edge(int u, int v, OperatorType x) {
        int anc = lca(u, v);
        update_base(u, anc, x, 0);
        update_base(v, anc, x, 0);
    }

    // lca で分解されたあとのパスについて、クエリを処理
    MonoidType query_base(int u, int v, bool f) {
        MonoidType ret = E0;
        
        while(1) {
            if(depth[u] < depth[v]) swap(u, v);

            // A (根に近い) op B (遠い) の順番 (B op A ではない)
            // u と v の chain が異なるならば、chain の端まで上って次へ
            if(chain[u] != chain[v]) {
                int lhs = 0, rhs = idx[u] + 1;
                ret = cmb_f(segs[ chain[u] ].query(lhs, rhs), ret);
                u = climb(u);
            }
            // lhs を含むか含まないか (f = 1 で含む)
            else {
                int lhs = idx[v] + 1 - f, rhs = idx[u] + 1;
                ret = cmb_f(segs[ chain[u] ].query(lhs, rhs), ret);
                break;
            }
        }
        return ret;
    }

    // 頂点 u - v 間の「点」に関するクエリ (単位元と関数も必要)
    MonoidType qry_vertex(int u, int v) {
        int anc = lca(u, v);
        MonoidType ret = E0;
        ret = cmb_f(ret, query_base(anc, u, 1));
        ret = cmb_f(ret, query_base(anc, v, 0));
        return ret;
    }

    // 頂点 u - v 間の「辺」に関するクエリ
    MonoidType qry_edge(int u, int v) {
        int anc = lca(u, v);
        MonoidType ret = E0;
        ret = cmb_f(ret, query_base(anc, u, 0));
        ret = cmb_f(ret, query_base(anc, v, 0));
        return ret;
    }
};

struct Query {
    int len, u, v, idx;
    Query() {}
    Query(int a, int b, int c, int d) : len(a), u(b), v(c), idx(d) {}
};

int main() {
    int N, Q; cin >> N >> Q;
    Graph<int> G(N);

    vector< vector< tuple<int, int, int> > > edges(N); 
    for(int i=0; i<N-1; i++) {
        int a, b, c, d; cin >> a >> b >> c >> d;
        a--; b--; c--;
        G[a].emplace_back(b, d);
        G[b].emplace_back(a, d);
        edges[c].emplace_back(a, b, d);
    }

    HLD<int, int> hl1(G), hl2(G), hl3(G);
    hl1.buildSegtree(0, 0,
                     [](int a, int b) { return a + b; },
                     [](int a, int b) { return a + b; },
                     [](int a, int b) { return a + b; },
                     [](int a, int x) { return a;     });
    hl2.buildSegtree(0, 0,
                     [](int a, int b) { return a + b; },
                     [](int a, int b) { return a + b; },
                     [](int a, int b) { return a + b; },
                     [](int a, int x) { return a;     });
    hl3.buildSegtree(0, 0,
                     [](int a, int b) { return a + b; },
                     [](int a, int b) { return a + b; },
                     [](int a, int b) { return a + b; },
                     [](int a, int x) { return a;     });
    
    for(int i=0; i<N; i++) {
        for(auto e : edges[i]) {
            int u, v, d; tie(u, v, d) = e;
            hl1.upd_edge(u, v, d);
        }
    }

    vector< vector<Query> > queries(N);
    for(int i=0; i<Q; i++) {
        int x, y, u, v; cin >> x >> y >> u >> v;
        x--; u--; v--;
        queries[x].emplace_back(y, u, v, i);
    }

    vector<int> ans(Q);
    for(int i=0; i<N; i++) {
        // insert
        for(auto e : edges[i]) {
            int u, v, d; tie(u, v, d) = e;
            hl2.upd_edge(u, v, 1);
            hl3.upd_edge(u, v, d);
        }

        for(auto q : queries[i]) {
            int d = q.len, u = q.u, v = q.v, i = q.idx;
            int distA = hl1.qry_edge(u, v);
            int cntCol = hl2.qry_edge(u, v);
            int cntDst = hl3.qry_edge(u, v);

            int res = distA - cntDst + cntCol * d;
            ans[i] = res;
        }

        // erase
        for(auto e : edges[i]) {
            int u, v, d; tie(u, v, d) = e;
            hl2.upd_edge(u, v, -1);
            hl3.upd_edge(u, v, -d);
        }
    }

    for(auto a : ans) cout << a << endl;
    return 0;
}


Measured memory_bytes: 181524