#include <iostream>
#include <functional>
#include <vector>
#include <memory>
#include <cstring>
using namespace std;
using llong = long long;

//===
// K-ary tree
template<class T, int K = 2, template<class> class Alloc = allocator>
struct PersistentArray {
    struct Node;
    using Leaf = T;
    struct Node {
        T dat;
        Node* ch[K] = {};

        Node() = default;
        Node(Node *np) {
            dat = np->dat;
            memcpy(ch, np->ch, sizeof(Node *) * K);
        };
    };

    Node *root;
    int arr_size;

    PersistentArray() = default;
    PersistentArray (Node *root, int arr_size):root(root), arr_size(arr_size) {};
    PersistentArray(int size, T d = T()):arr_size(size) {
        root = new Node();
        init(root, size, d);
    };
    Node *init(Node *np, int size, T d) {
        np->dat = d;
                             
        if (size == 1) return np;
        for (int i = 0; i < min(K, size); i++) {
            np->ch[i] = init(new Node(), (size + K - 1) / K, d);
        }
        return np;
    };

    T get(int idx) {
        return get(idx, root);
    };
    T get(int idx, Node *np) {
        if (idx == 0) return np->dat;
        return get((idx - 1) / K, np->ch[idx % K]);
    };
    
    PersistentArray set(int idx, const T &val) {
        return {set(idx, val, root), arr_size};
    };
    Node *set(int idx, const T &val, Node *np) {
        if (idx == 0) {
            Node *node = new Node(np);
            node->dat = val;
            return node;
        }
        else {
            Node *node = new Node(np);
            node->ch[idx % K] = set((idx - 1) / K, val, np->ch[idx % K]);
            return node;
        }
    };

    void destructive_set(int idx, T &val, Node *np) {
        if (idx == 0) np->dat = val;
        else destructive_set((idx - 1) / K, val, np->ch[idx % K]);
    };

    int size() {
        return arr_size;
    };
    PersistentArray get_array() {
        return *this;
    };
};
//===

//===
// definitely call build() before lca() and distance()
// 0-indexed
// climb(u, d): climb d steps towards root
struct DoublingTree {
    vector<vector<int> > G;
    vector<vector<int> > parent;
    vector<int> depth;
    int logn;

    DoublingTree(int n) {
        logn = 1;
        while ((1 << logn) < n) logn++;

        G.resize(n);
        parent.assign(logn, vector<int>(n, -1));
        depth.assign(n, -1);
    };
    DoublingTree(vector<vector<int> > &g) {
        DoublingTree(g.size());
        for (int i = 0; i < g.size(); i++) {
            G[i].resize(g[i].size());
            for (int j = 0; j < g[i].size(); j++) {
                G[i][j] = g[i][j];
            }
        }
    };

    void calc_depth(int u, int d, int par) {
        depth[u] = d++;
        parent[0][u] = par;
        for (int v:G[u]) {
            if (v == par) continue;
            calc_depth(v, d, u);
        }
    };
    void build(int root = 0) {
        calc_depth(root, 0, -1);
        for (int k = 1; k < logn; k++) {
            for (int u = 0; u < size(); u++) {
                if (parent[k - 1][u] == -1) continue;
                parent[k][u] = parent[k - 1][parent[k - 1][u]];
            }
        }
    };
    
    void add_edge(int u, int v) {
        G[u].push_back(v);
        G[v].push_back(u);
    };
    int climb(int u, int d) {
        int cnt = 0;
        while (d) {
            if (d & 1) u = parent[cnt][u];
            d >>= 1;
            cnt++;
        }
        return u;
    };
    // LowestCommonAncestor
    int lca(int u, int v) {
        if (depth[u] > depth[v]) swap(u, v);
        v = climb(v, depth[v] - depth[u]);

        if (u == v) return u;
        for (int k = logn - 1; k >= 0; k--) {
            if (parent[k][u] != parent[k][v]) {
                u = parent[k][u];
                v = parent[k][v];
            }
        }
        return parent[0][u];
    };
    inline int distance(int u, int v) {
        return depth[u] + depth[v] - depth[lca(u, v)] * 2;
    };
    inline int size() {
        return G.size();
    };
};
//===

llong n, q;
vector<vector<tuple<llong, llong, llong>>> g;
vector<PersistentArray<llong>> cnt; // [vertex][color]
vector<PersistentArray<llong>> c_dist;
vector<llong> dist;

void dfs(int u, int par) {
    for (auto dat:g[u]) {
        llong v, c, d;
        tie(v, c, d) = dat;
      
        if (v == par) continue;
        
        dist[v] = dist[u] + d;
        cnt[v] = cnt[u].set(c, cnt[u].get(c) + 1);
        c_dist[v] = c_dist[u].set(c, c_dist[u].get(c) + d);
      
        dfs(v, u);
    }
}

int main() {
    cin >> n >> q;
    g.resize(n + 1);
    cnt.resize(n + 1);
    dist.resize(n + 1, 0);
    c_dist.resize(n + 1);
    DoublingTree lca(n + 1);
  
    cnt[1] = PersistentArray<llong>(n + 1, 0ll);
    c_dist[1] = PersistentArray<llong>(n + 1, 0ll);

    for (int i = 0; i < n - 1; i++) {
        llong a, b, c, d;
        cin >> a >> b >> c >> d;
      
        g[a].push_back(make_tuple(b, c, d));
        g[b].push_back(make_tuple(a, c, d));
        lca.add_edge(a, b);
    }
    
    dfs(1, 0);
    lca.build(1);
  
    for (int i = 0; i < q; i++) {
        llong x, y, u, v;
        cin >> x >> y >> u >> v;
      
        llong ans = dist[u] + dist[v] - dist[lca.lca(u, v)] * 2;
        ans -= c_dist[u].get(x) + c_dist[v].get(x) - c_dist[lca.lca(u, v)].get(x) * 2;
        ans += y * (cnt[u].get(x) + cnt[v].get(x) - cnt[lca.lca(u, v)].get(x) * 2);
      
        cout << ans << endl;
    }
  
    return 0;
}

Measured memory_bytes: 157816