#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 <cmath>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <unordered_map>
#include <string>
#include <vector>

namespace {

using namespace std;

struct Edge {
    int from;
    int to;
    int color;
    int weight;
};

struct SqrtNode {
    int depth = 0;
    int parent = -1;
    int pcolor = -1;
    int pweight = -1;
    int jump_parent = -1;
    int jump_weight = 0;
    unordered_map<int, int> jump_color_count;
    unordered_map<int, int> jump_color_weight;
};

class SqrtColorfulTree {
public:
    explicit SqrtColorfulTree(const vector<vector<Edge>>& adj_tab_) :
            adj_tab(adj_tab_), N(adj_tab.size() - 1), nodes(N + 1) {
        int tree_height = dfs_tree_height(1, -1);
        jump_dist = int(sqrt(tree_height) + 0.9999);
        LOG("Jump dist is: {}\n", jump_dist);
        vector<Edge> path;
        dfs_build_nodes(1, path);
    }

    int colored_path_weight(int u, int v, int color, int new_weight) {
        int path_weight = 0;
        if (nodes[u].depth > nodes[v].depth)
            swap(u, v);
        while (nodes[v].depth > nodes[u].depth) {
            if (nodes[v].jump_parent != -1 && nodes[v].depth - jump_dist >= nodes[u].depth) {
                LOG("Jumping v alone\n");
                path_weight += jump_weight(v, color, new_weight);
                v = nodes[v].jump_parent;
            } else {
                path_weight += parent_weight(v, color, new_weight);
                v = nodes[v].parent;
            }
        }
        while (u != v) {
            if (nodes[u].jump_parent != -1 && nodes[u].jump_parent != nodes[v].jump_parent) {
                LOG("Jumping both nodes\n");
                path_weight += jump_weight(u, color, new_weight);
                path_weight += jump_weight(v, color, new_weight);
                u = nodes[u].jump_parent;
                v = nodes[v].jump_parent;
            } else {
                path_weight += parent_weight(u, color, new_weight);
                path_weight += parent_weight(v, color, new_weight);
                u = nodes[u].parent;
                v = nodes[v].parent;
            }
        }
        return path_weight;
    }

private:

    int dfs_tree_height(int subroot, int parent) {
        int max_child_depth = 0;
        for (const auto& e : adj_tab[subroot]) {
            if (e.to == parent)
                continue;
            max_child_depth = max(max_child_depth, dfs_tree_height(e.to, subroot));
        }
        return max_child_depth + 1;
    }

    void dfs_build_nodes(int cur_node_idx, vector<Edge>& path) {
        // Recurse first
        for (const Edge& e : adj_tab[cur_node_idx]) {
            if (!path.empty() && e.to == path.back().from)
                continue;
            path.emplace_back(e);
            dfs_build_nodes(e.to, path);
            path.pop_back();
        }
        // Build current node
        SqrtNode& cnode = nodes[cur_node_idx];
        cnode.depth = path.size();
        if (cnode.depth == 0)
            return;
        cnode.parent = path.back().from;
        cnode.pcolor = path.back().color;
        cnode.pweight = path.back().weight;
        if (cnode.depth % jump_dist == 0) {
            cnode.jump_parent = path[path.size() - jump_dist].from;
            cnode.jump_weight = 0;
            for (int i = 0; i < jump_dist; ++i) {
                const Edge& e = path[path.size() - 1 - i];
                cnode.jump_weight += e.weight;
                cnode.jump_color_count[e.color] += 1;
                cnode.jump_color_weight[e.color] += e.weight;
            }
        }
        LOG("Jump from {} to {}, jump weight: {}\n", cur_node_idx, cnode.jump_parent,
            cnode.jump_weight);
    }

    int jump_weight(int v, int color, int new_weight) const {
        int weight = nodes[v].jump_weight;
        auto color_weight_iter = nodes[v].jump_color_weight.find(color);
        auto color_count_iter = nodes[v].jump_color_count.find(color);
        if (color_weight_iter != nodes[v].jump_color_weight.end()) {
            weight -= color_weight_iter->second;
            weight += color_count_iter->second * new_weight;
        }
        return weight;
    }

    int parent_weight(int v, int color, int new_weight) const {
        return nodes[v].pcolor == color ? new_weight : nodes[v].pweight;
    }

    const vector<vector<Edge>>& adj_tab;
    const int N;
    int jump_dist;
    vector<SqrtNode> nodes;
};

int solve() {
    // handle IO here
    int N, Q;
    cin >> N >> Q;
    vector<vector<Edge>> adj_tab(N + 1); // 1-based index
    for (int i = 0; i < N - 1; ++i) {
        int u, v, color, weight;
        cin >> u >> v >> color >> weight;
        adj_tab[u].emplace_back(Edge{u, v, color, weight});
        adj_tab[v].emplace_back(Edge{v, u, color, weight});
    }
    SqrtColorfulTree sq_tree(adj_tab);
    for (int i = 0; i < Q; ++i) {
        int color, new_weight, u, v;
        cin >> color >> new_weight >> u >> v;
        cout << sq_tree.colored_path_weight(u, v, color, new_weight) << "\n";
    }
    return 0;
}

} // anonymous namespace

#ifdef OJ_MISC_OFFLINE

#include "epilog.h"

#else

int main() {
    return solve();
}

#endif


Measured memory_bytes: 36464