#include <bits/stdc++.h>

#define foreach(i,v) for(auto&& i: v)
#define all(x) (x).begin(), (x).end()

typedef std::vector< std::string > VS;
typedef std::vector<int> VI;
typedef long long ll;

template <class C> C& mini(C& a, C b) { if (b < a) a = b; return a; }
template <class C> C& maxi(C& a, C b) { if (a < b) a = b; return a; }

using namespace std;

template <typename T> void read(T& x) { x.read(); }
template <typename T> void read(vector<T>& v) { for (T& x: v) read(x); }
template <typename X, typename Y> void read(pair<X, Y>& p) { read(p.first); read(p.second); }
template <> void read(int& x) { scanf("%d", &x); }
template <> void read(unsigned int& x) { scanf("%u", &x); }
template <> void read(long long& x) { scanf("%lld", &x); }
template <> void read(unsigned long long& x) { scanf("%llu", &x); }
template <typename T> void readv(vector<T>& v) { int N; read(N); v.resize(N); read(v); }
template <typename T, typename ...Ts> void read(T& x, Ts&... args) { read(x); read(args...); }

class Node;
struct Edge {
    Node *n;
    int c, d;
};

struct Query {
    int u, v, c, d;
    ll ans;
};

struct Qinfo {
    int s;
    int c, d;
    ll *ans;
};

struct Cinfo {
    ll len;
    int count;
    Cinfo() : len(0), count(0) {}
};

struct Ctx {
    ll totalLen;
    map<int, Cinfo> color;
};

class Node {
public:
    vector<Edge> edges; /* should be filled by user */
    Node *parent;
    int h;
    int n;
    int index;
    vector<Qinfo> infos;

    void addEdge(Node *node, int c, int d) {
        edges.push_back(Edge{node, c, d});
    }

    void attach(int s, int c, int d, ll *ans) {
        infos.push_back(Qinfo{s, c, d, ans});
    }

    void process() {
        Ctx ctx;
        ctx.totalLen = 0;
        process(&ctx);
    }

    void process(Ctx *ctx) {
        foreach (q, infos) {
            auto it = ctx->color.find(q.c);
            ll r = ctx->totalLen;
            if (it != ctx->color.end()) {
                r -= it->second.len;
                r += (ll)it->second.count * q.d;
            }
            *q.ans += q.s * r;
        }

        foreach (e, edges) {
            int c = e.c; int l = e.d;
            ctx->totalLen += l;
            Cinfo& info = ctx->color[c];
            info.len += l;
            info.count += 1;

            e.n->process(ctx);

            if (info.count > 1) {
                info.len -= l;
                info.count -= 1;
            } else {
                ctx->color.erase(c);
            }
            ctx->totalLen -= l;
        }
    }

    void mkroot() {
        this->parent = nullptr;
        this->h = 0;
        stack<Node*> st;
        st.push(this);
        while (!st.empty()) {
            Node *c = st.top();
            int pi = -1;
            st.pop();
            for (int i = 0; i < c->edges.size(); i++) {
                Node *ch = c->edges[i].n;
                if (ch != c->parent) {
                    ch->parent = c;
                    ch->h = c->h + 1;
                    st.push(ch);
                } else {
                    pi = i;
                }
            }
            if (pi >= 0) {
                c->edges[pi] = c->edges.back();
                c->edges.pop_back();
            }
        }
    }
};

template <int MAXN>
class LCAt {
    static const int BLOCKSIZE = 0.5 * log2(MAXN) + 1;

    static int ilog2u(int n) {
        int ret = 32 - __builtin_clz(n);
        return (n & (n-2)) ? ret : ret - 1;
    }

    static int ilog2d(int n) {
        return 31 - __builtin_clz(n);
    }

    const vector<Node>& nodes;

    int *bt; // sparse table on blocks (relative minimum positions in blocks)
    int bt_step;
    int *h; // vertex height
    VI a; // dfs list
    int *a_pos; // positions in dfs list
    int *brmq; // rmq inside each block, indexed by block hash
    int *bhash; // block hashes

    inline int min_h(int i, int j) const {
        return h[a[i]] < h[a[j]] ? i : j;
    }

    inline int lca_in_block(int bl, int l, int r) const {
        return brmq[(bhash[bl]*BLOCKSIZE + l) * BLOCKSIZE + r] + bl*BLOCKSIZE;
    }

    void dfs (int v, int curh) {
        h[v] = curh++;
        a_pos[v] = a.size();
        a.push_back(v);
        foreach (e, nodes[v].edges) {
            dfs(e.n->index, curh);
            a.push_back(v);
        }
    }

public:
    LCAt(const vector<Node>& nodes, int root=0)
        :nodes(nodes)
    {
        if (nodes.size() > MAXN) {
            throw runtime_error("nodes.size() too big");
        }

        h = new int[nodes.size()];
        a_pos = new int[nodes.size()];

        dfs(root, 0);

        int sz = a.size();

        int log_sz = ilog2u(sz);
        int nBlocks = (sz + BLOCKSIZE - 1) / BLOCKSIZE;
        bt_step = ilog2u(nBlocks) + 1;

        bt = new int[bt_step * nBlocks];
        memset(bt, -1, sizeof(int) * bt_step * nBlocks);

        for (int i = 0, bl = 0, j = 0; i < sz; i++, j++) {
            if (j == BLOCKSIZE) {
                j = 0;
                bl++;
            }
            int& btv = bt[bl*bt_step];
            if (btv == -1 || min_h(i, btv) == i)
                btv = i;
        }

        for (int j = 1; j < bt_step; j++)
            for (int i = 0; i < nBlocks; i++) {
                int ni = i + (1<<(j-1));
                if (ni >= nBlocks)
                    bt[i*bt_step+j] = bt[i*bt_step+j-1];
                else
                    bt[i*bt_step+j] = min_h(bt[i*bt_step+j-1], bt[ni*bt_step+j-1]);
            }

        // calc hashes of blocks
        bhash = new int[nBlocks]();
        for (int i = 0, bl = 0, j = 0; i < sz || j < BLOCKSIZE; i++, j++) {
            if (j == BLOCKSIZE) {
                j = 0;
                bl++;
            }
            if (j > 0 && (i >= sz || min_h (i-1, i) == i-1))
                bhash[bl] |= 1<<(j-1);
        }

        // precalc RMQ inside each unique block
        brmq = new int[(1<<BLOCKSIZE)*BLOCKSIZE*BLOCKSIZE];
        memset(brmq, -1, sizeof(int)*BLOCKSIZE*BLOCKSIZE*(1<<BLOCKSIZE));
        for (int i = 0; i < nBlocks; i++) {
            int id = bhash[i];
            int* pbrmq = &brmq[id*BLOCKSIZE*BLOCKSIZE];
            if (*pbrmq != -1)
                continue;
            for (int l = 0; l < BLOCKSIZE; l++) {
                int *ppbrmq = &pbrmq[l*BLOCKSIZE];
                ppbrmq[l] = l;
                for (int r = l + 1; r < BLOCKSIZE; r++) {
                    ppbrmq[r] = ppbrmq[r-1];
                    if (i*BLOCKSIZE+r < sz)
                        ppbrmq[r] =
                            min_h(i*BLOCKSIZE+ppbrmq[r], i*BLOCKSIZE+r) - i*BLOCKSIZE;
                }
            }
        }
    }

    const Node* Find(const Node* n1, const Node* n2) const
    {
        int v1 = n1->index, v2 = n2->index;
        int l = a_pos[v1], r = a_pos[v2];
        if (l > r)
            swap (l, r);
        int bl = l/BLOCKSIZE, br = r/BLOCKSIZE;
        if (bl == br)
            return &nodes[a[lca_in_block(bl, l%BLOCKSIZE, r%BLOCKSIZE)]];
        int ans1 = lca_in_block(bl, l%BLOCKSIZE, BLOCKSIZE-1);
        int ans2 = lca_in_block(br, 0, r%BLOCKSIZE);
        int ans = min_h(ans1, ans2);
        if (bl < br - 1) {
            int pw2 = ilog2d(br-bl-1);
            int ans3 = bt[(bl+1)*bt_step + pw2];
            int ans4 = bt[(br-(1<<pw2))*bt_step + pw2];
            ans = min_h(ans, min_h(ans3, ans4));
        }
        return &nodes[a[ans]];
    }

    ~LCAt() {
        delete[] bt;
        delete[] h;
        delete[] a_pos;
        delete[] brmq;
        delete[] bhash;
    }
};

int main(int argc, const char* argv[]) {
    int N, Q;
    read(N, Q);
    vector<Node> nodes(N);
    for (int i = 0; i < N; i++)
        nodes[i].index = i;

    for (int i = 0; i < N-1; i++) {
        int a, b, c, d;
        read(a, b, c, d);
        a--; b--;
        nodes[a].addEdge(&nodes[b], c, d);
        nodes[b].addEdge(&nodes[a], c, d);
    }
    nodes[0].mkroot();

    LCAt<100500> lca(nodes);
    vector<Query> qq(Q);
    foreach (q, qq) {
        read(q.c, q.d, q.u, q.v);
        q.u--; q.v--;
        Node *root = (Node*)lca.Find(&nodes[q.u], &nodes[q.v]);
        root->attach(-2, q.c, q.d, &q.ans);
        nodes[q.u].attach(1, q.c, q.d, &q.ans);
        nodes[q.v].attach(1, q.c, q.d, &q.ans);
        q.ans = 0;
    }

    nodes[0].process();

    foreach(q, qq)
        printf("%lld\n", q.ans);

    return 0;
}


Measured memory_bytes: 34416