#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <bitset>
#include <array>
#include <vector>
#include <queue>
#include <set>
#include <cmath> // 変数名にy1が使えなくなるかも…。
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <limits>
#include <string>

typedef __int128_t int128_t;
std::istream &operator>>(std::istream& input, int128_t& value) { // int128_tの入力。入力が64bitに収まる前提。
    long long tmp; input >> tmp; value = tmp;
    return input;
}
std::ostream &operator<<(std::ostream& output, const int128_t value) { // int128_tの出力。出力が64bitに収まる前提。
    output << (long long)value;
    return output;
}

int N;
std::vector<std::vector<int>> edges[100000];
std::vector<int> edges2[100000];
 
std::vector<int> need[100000];
std::map<int, int> resnum[100000], ressum[100000];
 
int64_t dist[100000];
int64_t stk_num[100000], stk_sum[100000];
 
void dfs(int i, int p, int col, int dis){
    if(col >= 0){
        stk_num[col]++;
        stk_sum[col] += dis;
    }
 
    for(int c : need[i]){
        resnum[i][c] = stk_num[c];
        ressum[i][c] = stk_sum[c];
    }
 
    for(auto& e : edges[i]){
        int j = e[0], c = e[1], d = e[2];
        if(j == p) continue;
        dist[j] = dist[i] + d;
        dfs(j, i, c, d);
    }
    if(col >= 0){
        stk_num[col]--;
        stk_sum[col] -= dis;
    }
}
 
struct LCA{
    static const int BITLEN_MAX = 30;
    std::vector<int> parent[BITLEN_MAX];
    std::vector<int> depth;
    int bitlen;
 
    void initialize(int N, const std::vector<int> edges[]){
        int root = 0;
        bitlen = 1;
        while((1<<bitlen) < N) bitlen += 1;
        for(int i=0; i<bitlen; i++) parent[i].resize(N);
        depth.resize(N, -1);
 
        dfs(root, -1, 0, edges);
        for(int k=0; k<bitlen-1; k++){
            for(int v=0; v<N; v++){
                if(depth[v] == -1) continue;
                if(parent[k][v] < 0){
                    parent[k+1][v] = -1;
                }else{
                    parent[k+1][v] = parent[k][parent[k][v]];
                }
            }
        }
    }
 
    void dfs(int v, int p, int d, const std::vector<int> edges[]){
        parent[0][v] = p;
        depth[v] = d;
        for(auto u : edges[v]){
            if(u != p) dfs(u, v, d+1, edges);
        }
    }
 
    int calc_lca(int u, int v){
        if(depth[u] > depth[v]) std::swap(u, v);
        for(int k=0; k<bitlen; k++){
            if( ((depth[v]-depth[u]) >> k) & 1 ) v = parent[k][v];
        }
        if(u == v) return u;
        for(int k=bitlen-1; k>=0; k--){
            if(parent[k][u] != parent[k][v]){
                u = parent[k][u];
                v = parent[k][v];
            }
        }
        return parent[0][u];
    }
 
    int calc_dist(int u, int v){
        int l = calc_lca(u, v);
        return depth[u] + depth[v] - depth[l]*2;
    }
};
 
/*
int main(){
    int Q;
    cin >> N >> Q;
    for(int i=0; i<N-1; i++){
        int a, b, c, d;
        cin >> a >> b >> c >> d;
        a--; b--; c--;
        edges[a].push_back({b, c, d});
        edges[b].push_back({a, c, d});
        edges2[a].push_back(b);
        edges2[b].push_back(a);
    }
 
    LCA lca;
    lca.initialize(N, edges2);
 
    int X[100000], Y[100000], U[100000], V[100000], L[100000];
    for(int i=0; i<Q; i++){
        cin >> X[i] >> Y[i] >> U[i] >> V[i];
        X[i]--; U[i]--; V[i]--;
        L[i] = lca.calc_lca(U[i], V[i]);
        need[U[i]].push_back(X[i]);
        need[V[i]].push_back(X[i]);
        need[L[i]].push_back(X[i]);
    }
 
    dfs(0, -1, -1, 0);
 
    for(int i=0; i<Q; i++){
        int64_t a = U[i], b = V[i], l = L[i], c = X[i], d = Y[i];
        int64_t ad = dist[a] - ressum[a][c] + resnum[a][c] * d;
        int64_t bd = dist[b] - ressum[b][c] + resnum[b][c] * d;
        int64_t ld = dist[l] - ressum[l][c] + resnum[l][c] * d;
        int64_t ans = ad + bd - 2*ld;
        cout << ans << endl;
    }
    return 0;
}
*/

// binary search result
struct BSR {
    bool found; // 条件を満たす範囲が見つかったか。falseの場合、begin,endは意味を持たない。
    int128_t begin; // [begin, end]で条件が成り立つ。
    int128_t end;

    BSR(bool found_in, int128_t begin_in, int128_t end_in) : found(found_in), begin(begin_in), end(end_in) {}
};

template<class Fn> BSR binary_search(int128_t L, int128_t R, Fn func) {
    const bool res_L = func(L);
    const bool res_R = func(R);
    if (res_L && res_R) {
        return BSR(true, L, R);
    } else if (!res_L && !res_R) {
        return BSR(false, 0, 0);
    } else {
        int128_t lb = L;
        int128_t ub = R;
        while (lb + 1 < ub) {
            int128_t mid = (lb + ub) / 2;
            if (res_L == func(mid)) {
                lb = mid;
            } else {
                ub = mid;
            }
        }
        return res_L ? BSR(true, L, lb) : BSR(true, ub, R);
    }
}

// 木のクラス。
// 有向グラフと同様に表現する。
// （Tipsに含まれている関数で、こちらに移植していないものがあるので注意。）
class Tree_New {
    public:
    struct Edge { 
        int to; int64_t cost;
    };
    typedef std::vector<std::vector<Edge>> adj_list;
    adj_list G;
    int node_num;
    Tree_New() {}
    Tree_New(const int node_num_in) {
        node_num = node_num_in;
        G.resize(node_num);
    }
    void add_edge(const int from, const int to, const long long cost) {
        Edge edge; edge.to = to; edge.cost = cost;
        G[from].push_back(edge);
    }
    void add_edge_both(const int node1, const int node2, const long long cost) {
        add_edge(node1, node2, cost);
        add_edge(node2, node1, cost);
    }
    void cal_rank_child(int n, int r, int parent, std::vector<int>& rank) { // 辺が有向でも無向でも使える。呼び出すとき、parentは-1にしておくとよい。
        rank[n] = r;
        for (Edge e : G[n]) {
            if (e.to != parent) {
                cal_rank_child(e.to, r + 1, n, rank);
            }
        }
    }
    std::vector<int> cal_rank(const int root) { // root を根としたときのrankを計算する。
        assert(root < node_num);
        std::vector<int> rank; rank.resize(node_num, -1);
        cal_rank_child(root, 0, -1, rank);
        return rank;
    }

    void cal_parent_child(int n, int p, std::vector<int>& parent) {
        parent[n] = p;
        for (Edge e : G[n]) {
            if (e.to != p) {
                cal_parent_child(e.to, n, parent);
            }
        }
    };
    std::vector<int> cal_parent(const int root) { // root を根としたときの親を計算する。有向木では行うまでもない。rootの親はrootとしておく。
        assert(root < node_num);
        std::vector<int> parent; parent.resize(node_num, -1);
        cal_parent_child(root, root, parent);
        return parent;
    }
    std::vector<std::vector<int>> cal_parent_doubling(const int root) {
        // parentのダブリング構築。ret[i][n]は、nから2^iだけ親の方へ進んだ節点になる。
        std::vector<std::vector<int>> ret;
        std::vector<int> parent = cal_parent(root);
        ret.push_back(parent);
        while (true) {
            std::vector<int> pd; pd.resize(node_num);
            bool end_flag = true;
            for (int i = 0; i < node_num; i++) {
                if (ret.back()[i] == root || ret.back()[i] == -1) {
                    pd[i] = ret.back()[i];
                } else {
                    pd[i] = ret.back()[ret.back()[i]];
                    if (pd[i] != root) { 
                        end_flag = false;
                    }
                }
            }
            ret.push_back(pd);
            if (end_flag) { break; }
        }
        return ret;
    }
    int cal_ancestor(int node, int num, const std::vector<std::vector<int>>& parent_doubling) {
        // parent_doublingを利用して、nodeからnumだけ親のノードを求める。
        for (int i = 0; ; i++) {
            if (num % 2 == 1) {
                node = parent_doubling[i][node];
            }
            num /= 2;
            if (num == 0) { break; }
        }
        return node;
    }
    int lca(int u, int v, const std::vector<int>& rank, const std::vector<std::vector<int>>& parent_doubling) {
        // rank と parent_doublingから、least common ancestorを計算。
        if (rank[u] < rank[v]) {
            v = cal_ancestor(v, rank[v] - rank[u], parent_doubling);
        } else {
            u = cal_ancestor(u, rank[u] - rank[v], parent_doubling);
        }
        assert(rank[v] == rank[u]);
        if (u == v) { return u; }
        int lb = 0; int ub = rank[u];
        while (lb + 1 < ub) {
            int mid = (lb + ub) / 2;
            if (cal_ancestor(u, mid, parent_doubling) == cal_ancestor(v, mid, parent_doubling)) {
                ub = mid;
            } else {
                lb = mid;
            }
        }
        return cal_ancestor(u, ub, parent_doubling);
    }

    void dfs_in_order_child(int n, int parent, std::vector<std::pair<int, int>>& order) {
        order.push_back({parent, n});
        for (Edge e : G[n]) {
            if (e.to != parent) {
                dfs_in_order_child(e.to, n, order);
            }
        }
        order.push_back({n, parent});
    } 
    std::vector<std::pair<int, int>> dfs_in_order(const int root) { // root を根としたdfsで、通りがけ順に辺を並べる。
        assert(root < node_num);
        std::vector<std::pair<int, int>> order;
        for (Edge e : G[root]) {
            dfs_in_order_child(e.to, root, order);
        }
        return order;
    }
};

const int MAX_N = 1e5 + 10;
const int MAX_Q = 1e5 + 10;
//int N, Q;
int Q;
int a[MAX_N], b[MAX_N], c[MAX_N], d[MAX_N];

int x[MAX_Q], y[MAX_Q], u[MAX_N], v[MAX_N];

Tree_New tree = Tree_New(MAX_N);
std::map<std::pair<int, int>, int> edge_mp;

int color_num_tmp[MAX_N] = {};
int color_dist_tmp[MAX_N] = {};

std::vector<int> color_id[MAX_N], color_num[MAX_N], color_dist[MAX_N];
std::vector<int> first; 

int calc(int u, int nc, int nd) {
    if (u == 1) { return 0; }
    int ret = color_dist[0][first[u]];
    if (0 < color_id[nc].size()) {
        assert(color_id[nc].size() == color_dist[nc].size());
        assert(color_id[nc].size() == color_num[nc].size());
        auto bsr = binary_search(0, color_id[nc].size() - 1, [&](const int i){
            return color_id[nc][i] <= first[u];
        });
        if (bsr.found) {
            //std::cout << "bsr:" << bsr.end << " " << color_order[nc][bsr.end] << std::endl;
            ret -= color_dist[nc][bsr.end];
            ret += color_num[nc][bsr.end] * nd;
        }
    }
    assert(ret <= 1e9);
    return ret;
} 

int main(int argc, char **argv) {
    std::cin >> N >> Q;

    for (int i = 1; i <= N-1; i++) {
        std::cin >> a[i] >> b[i] >> c[i] >> d[i];
    }

    for (int i = 1; i <= Q; i++) {
        std::cin >> x[i] >> y[i] >> u[i] >> v[i];
    }

    for (int i = 1; i <= N - 1; i++) {
        tree.add_edge_both(a[i], b[i], d[i]);
        edge_mp[{a[i], b[i]}] = i;
        edge_mp[{b[i], a[i]}] = i;

        edges[a[i]-1].push_back({b[i]-1, c[i]-1, d[i]});
        edges[b[i]-1].push_back({a[i]-1, c[i]-1, d[i]});
        edges2[a[i]-1].push_back(b[i]-1);
        edges2[b[i]-1].push_back(a[i]-1);
    }

    auto rank = tree.cal_rank(1);
    auto pd = tree.cal_parent_doubling(1);

    LCA lca;
    lca.initialize(N, edges2);

    auto in_order = tree.dfs_in_order(1);
    assert(in_order.size() == (N-1)*2);

    first.resize(MAX_N, -1);
    std::vector<int> edge_ap; edge_ap.resize(MAX_N, 0);
    for (int i = 0; i < in_order.size(); i++) {
        if (first[in_order[i].second] < 0) {
            first[in_order[i].second] = i;
        }
        int ei = edge_mp[in_order[i]];
        if (edge_ap[ei]) {
            assert(edge_ap[ei] == 1);
            color_num_tmp[c[ei]]--;
            color_dist_tmp[c[ei]] -= d[ei];
            color_dist_tmp[0] -= d[ei];
        } else {
            color_num_tmp[c[ei]]++;
            color_dist_tmp[c[ei]] += d[ei];
            color_dist_tmp[0] += d[ei];
            edge_ap[ei]++;
        }
        color_id[c[ei]].push_back(i);
        color_num[c[ei]].push_back(color_num_tmp[c[ei]]);
        color_dist[c[ei]].push_back(color_dist_tmp[c[ei]]);
        color_dist[0].push_back(color_dist_tmp[0]);
    }

    int L[100000];
    for (int i = 1; i <= Q; i++) {
        L[i-1] = lca.calc_lca(u[i]-1, v[i]-1);
        need[u[i]-1].push_back(x[i]-1);
        need[v[i]-1].push_back(x[i]-1);
        need[L[i-1]].push_back(x[i]-1);
    }

    dfs(0, -1, -1, 0);
 
    for(int i = 1; i <= Q; i++) {
        int64_t a = u[i]-1, b = v[i]-1, l = L[i-1], c = x[i]-1, d = y[i];
        int64_t ad = dist[a] - ressum[a][c] + resnum[a][c] * d;
        int64_t bd = dist[b] - ressum[b][c] + resnum[b][c] * d;
        int64_t ld = dist[l] - ressum[l][c] + resnum[l][c] * d;
        int64_t ans = ad + bd - 2*ld;
        
        int anc = tree.lca(u[i], v[i], rank, pd);
        //assert(l+1 == anc);
        int ret = 0;
        ret += calc(u[i], x[i], y[i]);
        ret += calc(v[i], x[i], y[i]);
        ret -= calc(anc, x[i], y[i]) * 2;

        //assert(ans == ret);
        std::cout << ret << std::endl;
    }

    return 0;
}

Measured memory_bytes: 119076