#include <bits/stdc++.h>
#define err(args...) {}
#ifdef DEBUG
#include "_debug.cpp"
#endif
using namespace std;
using ll = long long;
using ld = long double;
template <typename T> using lim = numeric_limits<T>;
template <typename T> istream& operator>>(istream& is, vector<T>& a) { for(T& x : a) { is >> x; } return is; }
template <bool Directed, typename TEdge> struct graph {
    using EType = TEdge;
    vector<TEdge> edges;
    vector<vector<int>> adj;
    graph(int n) : adj(n + 1) {}
    graph(int n, int m) : graph(n) { edges.reserve(m); }
    TEdge& operator()(int i) { return edges[i]; }
    vector<int>& operator[](int u) { return adj[u]; }
    int size() { return adj.size() - 1; }
    void add_edge(const TEdge& e) {
        adj[e.u].push_back(edges.size());
        edges.push_back(e);
        if(not Directed) {
            adj[e.v].push_back(edges.size());
            edges.push_back(e.reverse());
        }
    }
};
template <typename graph> void dfs(graph& g,
const function<void(int)>& pre_node = [](int) {},
const function<void(typename graph::EType&)>& pre_edge = [](typename graph::EType&) {},
const function<void(typename graph::EType&)>& pos_edge = [](typename graph::EType&) {},
const function<void(int)>& pos_node = [](int) {},
int root = 0) {
    vector<bool> vis(g.size() + 1);
    function<void(int)> dfs = [&](int u) {
        vis[u] = true;
        pre_node(u);
        for(int i : g[u]) if(not vis[g(i).v]) {
            pre_edge(g(i)), dfs(g(i).v), pos_edge(g(i));
        }
        pos_node(u);
    };
    if(root) {
        dfs(root);
    } else {
        for(int u = 1; u <= g.size(); u++) if(not vis[u]) {
            dfs(u);
        }
    }
}
template <typename TEdge> struct flat_tree {
    vector<int> p, depth, head, d, f;
    flat_tree(graph<0, TEdge>& g) : p(g.size() + 1), depth(g.size() + 1), head(g.size() + 1), d(g.size() + 1), f(g.size() + 1) {
        vector<int> size(g.size() + 1, 1);
        dfs(g, [](int) {}, [&](TEdge& e) {
            p[e.v] = e.u;
            depth[e.v] = depth[e.u] + 1;
        }, [&](TEdge& e) {
            size[e.u] += size[e.v];
            if(size[e.v] >= size[g(g[e.u][0]).v]) {
                swap(e, g(g[e.u][0]));
            }
        });
        int timer = 1;
        dfs(g, [&](int u) {
            d[u] = timer++;
            head[u] = p[u] and u == g(g[p[u]][0]).v ? head[p[u]] : u;
        }, [](TEdge& e) {}, [](TEdge& e) {}, [&](int u) {
            f[u] = timer++;
        });
    }
    int lca(int u, int v) {
        if(head[u] == head[v]) {
            return depth[u] < depth[v] ? u : v;
        } else {
            return depth[head[u]] < depth[head[v]] ? lca(u, p[head[v]]) : lca(p[head[u]], v);
        }
    }
};
template <typename Iter, typename T> int gt(Iter L, Iter R, T v) { return upper_bound(L, R, v) - L; }
template <typename Iter, typename T> int ge(Iter L, Iter R, T v) { return lower_bound(L, R, v) - L; }
template <typename Iter, typename T> int lt(Iter L, Iter R, T v) { return lower_bound(L, R, v) - L - 1; }
template <typename Iter, typename T> int le(Iter L, Iter R, T v) { return upper_bound(L, R, v) - L - 1; }
template <typename Bst, typename T> typename Bst::const_iterator gt(const Bst& bst, T v) { return bst.upper_bound(v); }
template <typename Bst, typename T> typename Bst::const_iterator ge(const Bst& bst, T v) { return bst.lower_bound(v); }
template <typename Bst, typename T> typename Bst::const_iterator lt(const Bst& bst, T v) { return bst.lower_bound(v) == bst.begin() ? bst.end() : --bst.lower_bound(v); }
template <typename Bst, typename T> typename Bst::const_iterator le(const Bst& bst, T v) { return bst.upper_bound(v) == bst.begin() ? bst.end() : --bst.upper_bound(v); }
struct wedge {
    int u, v, c, d;
    wedge reverse() const { return {v, u, c, d}; }
};
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    int n, q;
    cin >> n >> q;
    graph<0, wedge> g(n);
    for(int i = 1; i < n; i++) {
        int a, b, c, d;
        cin >> a >> b >> c >> d;
        g.add_edge({a, b, c, d});
    }
    flat_tree<wedge> ft(g);
    vector<ll> s(2 * n + 1);
    vector<vector<pair<int, ll>>> s_color(n, {{0, 0}}), n_color(n, {{0, 0}});
    for(wedge& e : g.edges) {
        if(ft.p[e.v] == e.u) {
            s[ft.d[e.v]] = e.d;
            s[ft.f[e.v]] = -e.d;
            s_color[e.c].push_back({ft.d[e.v], e.d});
            s_color[e.c].push_back({ft.f[e.v], -e.d});
            n_color[e.c].push_back({ft.d[e.v], 1});
            n_color[e.c].push_back({ft.f[e.v], -1});
        }
    }
    partial_sum(s.begin(), s.end(), s.begin());
    for(int i = 0; i < n; i++) {
        sort(s_color[i].begin(), s_color[i].end());
        sort(n_color[i].begin(), n_color[i].end());
        for(int j = 1; j < s_color[i].size(); j++) {
            s_color[i][j].second += s_color[i][j - 1].second;
            n_color[i][j].second += n_color[i][j - 1].second;
        }
    }
    auto query = [&](int u, int x, int y) {
        int index = le(s_color[x].begin(), s_color[x].end(), make_pair(ft.d[u], lim<ll>::max()));
        return s[ft.d[u]] - s_color[x][index].second + n_color[x][index].second * y;
    };
    for(int i = 0; i < q; i++) {
        int x, y, u, v;
        cin >> x >> y >> u >> v;
        cout << query(u, x, y) + query(v, x, y) - 2 * query(ft.lca(u, v), x, y) << endl;
    }
    return 0;
}


Measured memory_bytes: 39660