#ifdef OJ_MISC_OFFLINE

#include "prolog.h"

#else
#define LOG
#define PRINT
#define LCERR
#include <iostream>
auto optimize_cpp_stdio = []() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);
    std::setvbuf(stdout, nullptr, _IOFBF, BUFSIZ);
    return 0;
}();
#endif

#include <algorithm>
#include <array>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <unordered_map>
#include <string>
#include <vector>

namespace {

using namespace std;

struct Edge {
    int from;
    int to;
    int c;
    int w;
};

struct Query {
    int c;
    int w;
    int n1;
    int n2;
};

struct Input {
    vector<Edge> edges;
    vector<Query> queries;
};

void dfs_root_undirected_tree(const vector<vector<Edge>>& graph, int cur, int parent,
                              vector<Edge>& res) {
    for (const auto& ed : graph[cur]) {
        if (ed.to == parent)
            continue;
        res.emplace_back(ed);
        dfs_root_undirected_tree(graph, ed.to, cur, res);
    }
}

vector<Edge> root_undirected_tree(const vector<Edge>& edges) {
    const int N = edges.size() + 1;
    vector<vector<Edge>> graph(N + 1);
    for (const auto& e : edges) {
        graph[e.from].emplace_back(e);
        Edge rev_e = e;
        swap(rev_e.from, rev_e.to);
        graph[e.to].emplace_back(rev_e);
    }
    vector<Edge> res;
    dfs_root_undirected_tree(graph, 1, -1, res);
    return res;
}

class CustomizedRangeMinSegTree {
public:
    struct Node {
        int min = 0;
        int idx = 0;
    };

    const int INF = 1000000000;

    explicit CustomizedRangeMinSegTree(const vector<int>& pnums)
            : N(pnums.size()), nums(pnums), st(4 * N) {
        build(0, 0, nums.size());
    }

    /**
     * Query for the value and (one of the) position of the minimum on range [l, h)
     * @return value, position
     */
    pair<int, int> query(int ql, int qh) { return query(0, 0, nums.size(), ql, qh); }

private:

    pair<int, int> query(int id, int l, int h, int ql, int qh) {
        if (h <= ql || qh <= l) return make_pair(INF, 0);
        if (ql <= l && h <= qh) return make_pair(st[id].min, st[id].idx);
        int mid = l + (h - l) / 2;
        auto lret = query(lc(id), l, mid, ql, qh);
        auto rret = query(rc(id), mid, h, ql, qh);
        return lret.first <= rret.first ? lret : rret;
    }

    void build(int id, int l, int h) {
        Node& cn = st[id];
        if (l + 2 > h) {
            st[id] = Node{nums[l], l};
            return;
        }
        int mid = l + (h - l) / 2;
        build(lc(id), l, mid);
        build(rc(id), mid, h);
        cn = st[lc(id)].min <= st[rc(id)].min ? st[lc(id)] : st[rc(id)];
    }

    static int lc(int id) { return id * 2 + 1; }

    static int rc(int id) { return id * 2 + 2; }

    const int N;
    const vector<int>& nums;
    vector<Node> st;
};

class ColorfulRangeSum {
public:
    struct Node {
        int total_w = 0;
        unordered_map<int, int> clr_cnt;
        unordered_map<int, int> clr_w;
    };

    explicit ColorfulRangeSum(const vector<pair<Edge, int>>& euc_edges)
            : N(euc_edges.size()), euc_edges_(euc_edges), st_(N * 4) {
        build(0, 0, N);
    }

    int query_with_color(int ql, int qh, int xclr, int nw) const {
        return query_with_color(ql, qh, xclr, nw, 0, 0, N);
    }

private:

    void build(int idx, int l, int h) {
        auto& nod = st_[idx];
        if (l + 2 > h) {
            // range length is 1, we won't generate ranges of length zero during recursion
            const Edge& e = euc_edges_[l].first;
            int sign = euc_edges_[l].second;
            nod.total_w = e.w * sign;
            nod.clr_cnt[e.c] = sign;
            nod.clr_w[e.c] = e.w * sign;
            return;
        }
        int mid = l + (h - l) / 2;
        build(idx * 2 + 1, l, mid);
        build(idx * 2 + 2, mid, h);
        build_merge(st_[idx * 2 + 1], st_[idx * 2 + 2], &nod);
        LOG("Node {}, l {}, h {}, total_w {}, numclr {}\n", idx, l, h, nod.total_w,
            nod.clr_cnt.size());
    }

    void build_merge(const Node& lnod, const Node& rnod, Node* nod) {
        nod->total_w = lnod.total_w + rnod.total_w;
        nod->clr_cnt.clear();
        nod->clr_w.clear();
        for (const auto& pair : lnod.clr_cnt) {
            nod->clr_cnt.insert(pair);
        }
        for (const auto& pair : lnod.clr_w) {
            nod->clr_w.insert(pair);
        }
        for (const auto& pair : rnod.clr_cnt) {
            nod->clr_cnt[pair.first] += pair.second;
            if (nod->clr_cnt[pair.first] == 0) {
                nod->clr_cnt.erase(pair.first);
            }
        }
        for (const auto& pair : rnod.clr_w) {
            nod->clr_w[pair.first] += pair.second;
            if (nod->clr_w[pair.first] == 0) {
                nod->clr_w.erase(pair.first);
            }
        }
    }

    int query_with_color(int ql, int qh, int xclr, int nw, int idx, int l, int h) const {
        if (l >= qh || h <= ql) return 0;
        if (ql <= l && h <= qh) {
            int sum_weight = st_[idx].total_w;
            auto cnt_iter = st_[idx].clr_cnt.find(xclr);
            auto w_iter = st_[idx].clr_w.find(xclr);
            sum_weight -= w_iter == st_[idx].clr_w.end() ? 0 : w_iter->second;
            sum_weight += cnt_iter == st_[idx].clr_cnt.end() ? 0 : cnt_iter->second * nw;
            return sum_weight;
        }
        int mid = l + (h - l) / 2;
        return query_with_color(ql, qh, xclr, nw, idx * 2 + 1, l, mid) +
               query_with_color(ql, qh, xclr, nw, idx * 2 + 2, mid, h);
    }

    const int N;
    const vector<pair<Edge, int>>& euc_edges_;
    vector<Node> st_;
};

class LCATreeWithPathSum {
public:
    struct Node { // Node of the input tree, not the segment tree
        vector<Edge> edges;
        int depth = 0;
        int euc_idx = -1; // Index of the first visit of this node on the euclidean tour
    };

    explicit LCATreeWithPathSum(const vector<Edge>& dir_edges) : N(int(dir_edges.size() + 1)) {
        build_tree(dir_edges);
        annotate_depth(root, 0);
        build_euc_tour(root);
        rmin = make_unique<CustomizedRangeMinSegTree>(euc_depth);
        clr_rsum = make_unique<ColorfulRangeSum>(euc_edges);
    }

    int lca(int n1, int n2) const {
        if (n1 == n2) return n1;
        int l = tree[n1].euc_idx;
        int h = tree[n2].euc_idx;
        if (l > h) swap(l, h);
        h += 1;
        auto rmin_ret = rmin->query(l, h);
        return euc_tour[rmin_ret.second];
    }

    int colored_path_weight(int n, int xclr, int nw) const {
        return clr_rsum->query_with_color(0, tree[n].euc_idx, xclr, nw);
    }

private:

    void build_tree(const vector<Edge>& edges) {
        tree.resize(N + 1); // 0 is not used
        vector<int> indegree(N + 1); // for finding root
        for (const auto& e : edges) {
            tree[e.from].edges.emplace_back(e);
            indegree[e.to] += 1;
        }
        for (int i = 1; i <= N; ++i) {
            if (indegree[i] == 0) {
                root = i;
                break;
            }
        }
    }

    void annotate_depth(int cur, int depth) {
        tree[cur].depth = depth;
        for (const auto& e : tree[cur].edges) {
            annotate_depth(e.to, depth + 1);
        }
    }

    void build_euc_tour(int cur) {
        tree[cur].euc_idx = int(euc_tour.size());
        auto add_cur_to_tour = [this, cur]() {
            euc_tour.emplace_back(cur);
            euc_depth.emplace_back(tree[cur].depth);
        };
        add_cur_to_tour();
        for (const auto& e : tree[cur].edges) {
            euc_edges.emplace_back(e, 1);
            LOG("euc edge: {} to {}, {}\n", e.from, e.to, 1);
            build_euc_tour(e.to);
            euc_edges.emplace_back(e, -1);
            LOG("euc edge: {} to {}, {}\n", e.from, e.to, -1);
            add_cur_to_tour();
        }
    }

    const int N;
    vector<Node> tree;
    int root = 0;
    vector<int> euc_tour; // node index seq of euc tour
    vector<int> euc_depth; // node depth seq of euc tour
    vector<pair<Edge, int>> euc_edges; // <edge, sign> seq of euc tour
    unique_ptr<CustomizedRangeMinSegTree> rmin;
    unique_ptr<ColorfulRangeSum> clr_rsum;
};

vector<int> colorful_tree(const Input& input) {
    vector<Edge> directed_edges = root_undirected_tree(input.edges);
    LCATreeWithPathSum lcatree{directed_edges};
    vector<int> result;
    for (const Query& q : input.queries) {
        int lca = lcatree.lca(q.n1, q.n2);
        int p1 = lcatree.colored_path_weight(q.n1, q.c, q.w);
        int p2 = lcatree.colored_path_weight(q.n2, q.c, q.w);
        int plca = lcatree.colored_path_weight(lca, q.c, q.w);
        LOG("lca:{} p1:{} p2:{} plca:{}\n", lca, p1, p2, plca);
        result.emplace_back(p1 + p2 - 2 * plca);
    }
    return result;
}

int solve() {
    // handle IO here
    size_t N, Q;
    cin >> N >> Q;
    Input input;
    input.edges.resize(N - 1);
    input.queries.resize(Q);
    for (size_t i = 0; i < N - 1; ++i) {
        Edge& e = input.edges[i];
        cin >> e.from >> e.to >> e.c >> e.w;
    }
    for (size_t i = 0; i < Q; ++i) {
        Query& q = input.queries[i];
        cin >> q.c >> q.w >> q.n1 >> q.n2;
    }

    auto result = colorful_tree(input);
    for (int r : result) {
        cout << r << '\n';
    }
    return 0;
}

} // anonymous namespace

#ifdef OJ_MISC_OFFLINE

#include "epilog.h"

#else

int main() {
    return solve();
}

#endif


Measured memory_bytes: 298824