import std.stdio, std.conv, std.string, std.array, std.math, std.regex, std.range, std.ascii, std.numeric, std.random;
import std.typecons, std.functional, std.traits;
import std.algorithm, std.container;
import core.stdc.stdlib, core.bitop;

struct Node{
    Edge[] connect;
    long[] colorReq;
    long cost;
    Color[] colors;
}

struct Edge{
    long to;
    long color;
    long cost;
    long from;
}
struct Req{
    long color, newCost, u, v;
}
struct Color{
    long color, count, sumCost;
}

void main()
{
    auto N = scanElem;
    auto Q = scanElem;

    Node[] nodes = new Node[N+1];
    foreach(i;0..N-1)
    {
        auto a = scanElem;
        auto b = scanElem;
        auto c = scanElem;
        auto d = scanElem;

        nodes[a].connect ~= Edge(b, c, d, a);
        nodes[b].connect ~= Edge(a, c, d, b);
    }

    long[] vs;
    long[] depth;
    long[] id = new long[N+1];
    dfs!(
        (e, d){
            id[e.to] = vs.length;

            vs ~= e.to;
            depth ~= d;
        },
        (e, d){
            vs ~= e.to;
            depth ~= d;
        },
        (n, d){ }
    )(nodes);

    auto tree = SegTreeRMQ!(long, "a<b")(depth);
    
    Req[] reqs;
    foreach(i;0..Q)
    {
        auto req = Req(scanElem, scanElem, scanElem, scanElem);
        auto u = id[req.u];
        auto v = id[req.v];
        if(u>v)swap(u,v);
        auto w = vs[tree.getIndex(u,v)];

        nodes[req.u].colorReq ~= req.color;
        nodes[req.v].colorReq ~= req.color;
        nodes[w].colorReq ~= req.color;

        reqs ~= req;
    }

    foreach(ref e; nodes)
        e.colorReq = sort(e.colorReq).uniq.array;

    long[] colorCount = new long[N];
    long[] colorNum = new long[N];
    long nowCost;
    dfs!(
        (e,d){
            colorCount[e.color]++;
            colorNum[e.color] += e.cost;
            nowCost += e.cost;

            nodes[e.to].cost = nowCost;
            foreach(c; nodes[e.to].colorReq)
            {
                nodes[e.to].colors ~= Color(c, colorCount[c], colorNum[c]);
            }
        },
        (e,d){
        },
        (e,d){
            colorCount[e.color]--;
            colorNum[e.color] -= e.cost;
            nowCost -= e.cost;
        }
    )(nodes);
    foreach(ref e; nodes) sort!"a.color<b.color"(e.colors);

    foreach(req; reqs)
    {
        auto u = id[req.u];
        auto v = id[req.v];
        if(u>v)swap(u,v);
        auto w = vs[tree.getIndex(u,v)];

        println(vs);
        println(depth);
        println(id);
        println(nodes);
        println(req);
        auto uColor = nodes[req.u].colors.assumeSorted!"a.color<b.color".equalRange(Color(req.color))[0];
        auto vColor = nodes[req.v].colors.assumeSorted!"a.color<b.color".equalRange(Color(req.color))[0];
        auto wColor = nodes[w    ].colors.assumeSorted!"a.color<b.color".equalRange(Color(req.color))[0];

        auto count = uColor.count + vColor.count - 2 * wColor.count;
        auto cost  = uColor.sumCost  + vColor.sumCost  - 2 * wColor.sumCost ;

        auto nc = 
        nodes[req.u].cost
        + nodes[req.v].cost
        - 2 * nodes[w].cost;
        println(nc);
        println("index: ", tree.getIndex(u,v));
        println("req.u: ", req.u, " req.v: ", req.v);
        println("u: ", u, " v: ", v, " w: ", w);
        println("c: ", nodes[req.u].cost, " c: ", nodes[req.v].cost, " c: ", nodes[w].cost);
        println("count: ", count);
        println("cost: ", cost);
        writeln(nc + count * req.newCost - cost);
    }
}

void dfs(alias preF, alias backF, alias postF)(const Node[] nodes)
{
    void func(const Node[] nodes, Edge cameEdge, long depth){
        binaryFun!preF(cameEdge, depth);
        foreach(c; nodes[cameEdge.to].connect)
        {
            if(c.to==cameEdge.from)continue;
            func(nodes, c, depth+1);
            binaryFun!backF(cameEdge, depth);
        }
        binaryFun!postF(cameEdge, depth);
    }
    func(nodes, Edge(1,0,0,-1), 0);
}

struct SegTreeRMQ(T, alias pred)
if(is(bool==typeof(binaryFun!pred(T.init, T.init))))
{
    alias TreeElement = Tuple!(long, T);
    alias STree = SegTree!(TreeElement, TreeElement(-1, T.init), (a,b){
        if(a[0]==-1)return b;
        if(b[0]==-1)return a;
        return binaryFun!pred(a[1], b[1])?a:b;
    });

    STree tree;

    this(E)(E arr)if(isInputRange!E&&hasLength!E)
    {
        tree = STree(zip(iota(0L,cast(long)arr.length) , arr).array);
    }
    void update(long a, T value){
        tree.update(a, TreeElement(value, a));
    }
    long getIndex(long a, long b){
        return tree[a..b][0];
    }
    string toString(){
        return tree.toString;
    }
}

unittest{
    assert(is(SegTreeRMQ!(long, "a<b")));
    assert(!is(SegTreeRMQ!(long, "a+b")));

    alias RmQ = SegTreeRMQ!(long, "a<b");
    assert(is(typeof(RmQ(generate!(()=>1).take(100)))));
    assert(!is(typeof(RmQ(generate!(()=>1)))));
}
unittest{
    alias RmQ = SegTreeRMQ!(long, "a<b");

    auto rmq = RmQ([1,4,6,3,2,7,8,5,9,0]);
    assert(rmq.getIndex(2, 5)==4);

    rmq.update(4, 100);
    assert(rmq.getIndex(2, 5)==3);

    assert(rmq.getIndex(0, 10)==9);
}

struct SegTree(T, T UNIT, alias pred)
if(is(T==typeof(binaryFun!pred(T.init,T.init))))
{
    alias F = binaryFun!pred;
    long count;
    T[] tree;
    long level;

    this(InputRange)(InputRange arr)
    if(isInputRange!InputRange&&hasLength!InputRange)
    {
        count = arr.length;
        level = calcLevel(count);
        tree = build(arr);
        updateAll();
    }

    void update(long index, T value)
    {
        size_t idx = cast(size_t)index;
        tree[levelOffset(level-1)+idx] = value;
        foreach_reverse(l; 0..level-1)
        {
            idx /= 2;
            auto ofs = levelOffset(l);
            auto underOfs = levelOffset(l+1);
            tree[ofs+idx] = F(tree[underOfs+idx*2],tree[underOfs+idx*2+1]);
        }
    }

    T opIndex(size_t[2] idx)
    {
        return get(idx[0], idx[1], 0);
    }
    size_t[2] opSlice(size_t dim : 0)(size_t a, size_t b){ return [a,b]; }
    size_t opDollar(size_t dim : 0)(){ return count; }

private:

    long calcLevel(size_t count) pure
    {
        auto level = 0;
        while(2^^level<count){
            level++;
        }
        level++;
        return level;
    }

    T[] build(InputRange)(InputRange arr) pure
    {
        T[] res;
        res.length = 2^^level;

        //copyの返り値は余ったRange
        arr.copy(res[levelOffset(level-1)..$])[] = UNIT;

        return res;
    }

    void updateAll()
    {
        foreach_reverse(l; 0..level-1)
        {
            foreach(idx; 0..(2^^l))
            {
                auto ofs = levelOffset(l);
                auto underOfs = levelOffset(l+1);
                tree[ofs+idx] = F(tree[underOfs+idx*2],tree[underOfs+idx*2+1]);
            }
        }
    }

    size_t levelOffset(size_t l) pure
    {
        assert(0<=l&&l<level);
        size_t res;
        foreach(i;0..l) res += 2^^i;
        return res;
    }

    T get(long s, long t, long l)
    // in(0<=s&&t<=N&&s<=t)
    {
        if(s==t)return UNIT;

        auto c = (2^^(level-1))/(2^^l);
        auto left = (s+c-1)/c;
        auto right = t/c;

        if(left>right) return get(s,t,l+1);

        T res = UNIT;
        res = F(res, get(s, left*c, l+1));
        if(left<right)
            res = F(res, tree[levelOffset(l)+left]);
        res = F(res, get(right*c, t, l+1));
        return res;
    }

    string toString()
    {
        string res;
        foreach(l;0..level)
        {
            auto arr = tree[levelOffset(l)..levelOffset(l+1)];
            foreach(elem; arr)
            {
                string elemStr = elem.to!string;

                static if(is(typeof(elem.isInf)))
                {
                    if(elem.isInf)
                    {
                        elemStr = "*";
                    }
                }
                res ~= elemStr ~ ' ';
            }
            res ~= '\n';
        }
        return res;
    }
}

unittest{
    assert(is(SegTree!(long, 0L, max)));
    assert(is(SegTree!(Tuple!(long,long), tuple(1L,0L), (a,b)=>tuple(a[0]*b[0],a[1]+b[1]))));
    assert(is(SegTree!(Tuple!(long,long), tuple(1L,0L), "tuple(a[0]*b[0],a[1]+b[1])")));
    assert(!is(SegTree!(long, 0L, "a~b")));
    assert(!is(SegTree!(long, 0L, ()=>10)));
    assert(!is(SegTree!(long, "unit", max)));
}
unittest{
    auto tree = SegTree!(long, 0L, "a+b")(iota(0L, 100L).map!"a+1");
    assert(tree[0..1]==1);
    assert(tree[50..51]==51);
    assert(tree[99..100]==100);
    assert(tree[0..100]==5050);
    assert(tree[3..98]==5050-(1+2+3)-(99+100));
    assert(tree[3..98]==tree[0..100]-tree[0..3]-tree[98..100]);
    assert(tree[50..80]==tree[50..60]+tree[60..70]+tree[70..80]);
    assert(tree[1..4]==2+3+4);
    assert(tree[0..32]==tree[0..24]+tree[24..34]-tree[32..34]);
    assert(tree[16..32]==tree[0..32]-tree[0..13]-tree[13..16]);

    assert(tree[0..$]==tree[0..100]);
}
unittest{
    struct Data{
        long x, y;
    }

    auto tree = SegTree!(Data, Data(0,INF), (a,b)=>Data(a.x+b.x,min(a.y,b.y)))([Data(1,2),Data(2,3),Data(3,4),Data(4,5)]);

    assert(tree[1..3]==Data(5,3));

    tree.update(2, Data(100,0));
    assert(tree[1..4]==Data(106,0));

    tree.update(1, Data(1,INF));
    tree.update(2, Data(1,INF));
    tree.update(3, Data(2,INF));
    assert(tree[1..4]==Data(4,INF));
    assert(tree[0..4]==Data(5,2));
}


enum INF = long.max/5;
enum MOD = 10^^9+7L;

bool isInf(long v) pure
{
    return v>=INF/5;
}

long mod(long n, long mod) pure
{
    return (n%mod+mod)%mod;
}

alias MInt = ModInt!MOD;

struct ModInt(alias Mod)
if(isIntegral!(typeof(Mod)) && isPrime(Mod))
{
    long value;

    this(ModInt!Mod v)
    {
        value = v.value;
    }
    this(long v)
    {
        value = mod(v, Mod);
    }

    private static ModInt!Mod inv(ModInt!Mod v) pure
    {
        ModInt res=1;
        long p = Mod-2;
        while(p!=0)
        {
            if(p&1)res *= v;
            v *= v;
            p >>= 1;
        }
        return res;
    }

    ModInt!Mod opBinaryRight(string op, T)(T v) const
    if((op=="+"||op=="-"||op=="*")&&isIntegral!T)
    {
        return ModInt!Mod(mixin("v"~op~"value"));
    }
    ModInt!Mod opBinary(string op, T)(T v) const
    if((op=="+"||op=="-"||op=="*")&&(is(T==ModInt!Mod)||isIntegral!T))
    {
        return ModInt!Mod(mixin("value"~op~"v"));
    }

    ModInt!Mod opBinaryRight(string op, T)(T v) const
    if((op=="/")&&isIntegral!T)
    {
        return v * inv(this);
    }
    ModInt!Mod opBinary(string op, T)(T v) const
    if((op=="/")&&(is(T==ModInt!Mod)||isIntegral!T))
    {
        return this * inv(ModInt(v));
    }

    void opAssign(T)(T v)
    if(isIntegral!T)
    {
        this = ModInt!Mod(v);
    }

    void opOpAssign(string op, T)(T v)
    {
        this = mixin("this"~op~"v");
    }

    bool opEquals(T)(const T v) const
    if(is(T==ModInt!Mod)||isIntegral!T)
    {
        return value == ModInt(v).value;
    }

    long opCast() const {
        return value;
    }

    string toString() const {
        return value.to!string;
    }
}
unittest
{
    assert(is(ModInt!MOD));
    assert(is(ModInt!17));
    assert(!is(ModInt!0));
    assert(!is(ModInt!10));
}
unittest
{
    alias MInt = ModInt!13;
    assert(MInt.inv(MInt(4))==10);
    assert(MInt.inv(MInt(6))==11);
    assert(MInt.inv(MInt(12))==12);
}
unittest
{
    alias MInt = ModInt!13;
    MInt value;
    value = MInt(14) + MInt(18);
    assert(value==6);
    value = 14 - MInt(28);
    assert(value==12);
    value = MInt(17) * -19;
    assert(value==2);

    value = MInt(7) / 4;
    assert(value==5);
    value = 8 / MInt(4);
    assert(value==2);
    value = 9;
    value /= 4;
    assert(value==12);

    value = 29-MInt(16);
    assert(value==0);
    value = MInt(13)*5;
    assert(value==0);
    value = 0;
    assert(value==0);

    value = 3;
    value += MInt(11);
    assert(value==1);
    value -= 7;
    assert(value==7);
    value *= MInt(4);
    assert(value==2);
    value = 23;
    assert(value == cast(long)value);
}
unittest
{
    ModInt!MOD value;
    value = MOD-1;
    assert(value==MOD-1);
    value = MOD;
    assert(value==0);
}

struct Grid(T){
    private T[] grid;
    size_t width, height;
    private size_t stride;

    private this(T[] g, size_t w, size_t h, size_t s)
    {
        grid = g;
        width = w;
        height = h;
        stride = s;
    }

    this(size_t w, size_t h)
    {
        this(new T[w*h], w, h, w);
    }

    void setRow(InputRange)(size_t y, InputRange range)
    if(isInputRange!InputRange && is(typeof(T.init=range.front)))
    {
        foreach(x; 0..width)
        {
            assert(!range.empty);
            this[x, y] = range.front;
            range.popFront;
        }
    }

    void setCol(InputRange)(size_t x, InputRange range)
    if(isInputRange!InputRange && is(typeof(T.init=range.front)))
    {
        foreach(y; 0..height)
        {
            assert(!range.empty);
            this[x, y] = range.front;
            range.popFront;
        }
    }

    Grid!T copy(){
        if(width == stride){
            return Grid!T(grid.dup, width, height, stride);
        }

        auto res = Grid!T(width, height);
        foreach(y;0..height)
        foreach(x;0..width)
        {
            res[x, y] = this[x, y];
        }
        return res;
    }

    ref T opIndex(size_t x, size_t y)
    {
        return grid[x+stride*y];
    }
    ref T opIndex(E)(E e)
        if(
            isIntegral!(typeof(e.x)) &&
            isIntegral!(typeof(e.y)))
    {
        return this[e.x, e.y];
    }

    Grid!T opIndex(size_t[2] r1, size_t[2] r2)
    {
        auto startOffset = r1[0] + r2[0]*stride;
        auto endOffset = r1[1] + (r2[1] - 1)*stride;

        auto resGrid = grid[startOffset .. endOffset];
        auto resStride = stride;
        auto resWidth = r1[1] - r1[0];
        auto resHeight = r2[1] - r2[0];
        return Grid!T(resGrid, resWidth, resHeight, resStride);
    }
    
    Grid!T opIndex(size_t[2] r1, size_t j) { return opIndex(r1, [j, j+1]); }
    Grid!T opIndex(size_t i, size_t[2] r2) { return opIndex([i, i+1], r2); }

    size_t[2] opSlice(size_t dim)(size_t start, size_t end)
    if (dim == 0 || dim == 1)
    // in { assert(start >= 0 && end <= this.opDollar!dim); }
    body{
        return [start, end];
    }

    size_t opDollar(size_t dim : 0)() const { return width; }
    size_t opDollar(size_t dim : 1)() const { return height; }

    string toString() {
        long count;
        foreach(y; 0..height)
        {
            foreach(x; 0..width)
            {
                long eCount = this[x, y].to!string.length;
                static if(is(typeof(this[x,y].isInf)))
                {
                    if(this[x,y].isInf)
                    {
                        eCount = 1;
                    }
                }
                count = max(count, eCount);
            }
        }
        count++;

        string res = "\n";
        foreach(y; 0..height)
        {
            foreach(x; 0..width)
            {
                string joinStr = this[x, y].to!string;
                static if(is(typeof(this[x,y].isInf)))
                {
                    if(this[x,y].isInf)
                    {
                        joinStr = "*";
                    }
                }
                foreach(i;0..count-joinStr.length)
                {
                    res ~= ' ';
                }
                res ~= joinStr;
            }
            res ~= '\n';
        }
        return res;
    }
}

long lcm(long a, long b) pure
{
    return a * b / gcd(a, b);
}

struct UnionFind{
    private long[] arr;

    @disable this();
    this(long n){
        arr.length = cast(size_t)n;
        arr[] = -1;
    }

    void merge(long a, long b)
    {
        if(same(a,b)) return;
        arr[root(a)] = root(b);
    }

    bool same(long a, long b)
    {
        return root(a)==root(b);
    }

    private size_t root(long a)
    {
        size_t i = cast(size_t)a;
        if(arr[i] == -1) return i;
        return arr[i] = root(arr[i]);
    }
}

unittest{
    assert(is(typeof(UnionFind(10))));
    assert(!is(typeof(UnionFind())));

    auto uf = UnionFind(10);
    uf.merge(2,3);
    assert(uf.same(2,3));
    uf.merge(3,4);
    uf.merge(1,5);
    uf.merge(5,6);
    uf.merge(6,7);
    assert(!uf.same(4,7));
    uf.merge(1,2);
    assert(uf.same(4,7));
}

enum TermColor{
    Blue,
    Red,
    Purple,
    Gray,
    Green,
    Brown,
    Default,
}

string termStr(TermColor c) pure
{
    switch(c)
    {
        case TermColor.Blue:   return "\033[38;2;122;158;194m";
        case TermColor.Red:    return "\033[38;2;204;130;66m";
        case TermColor.Purple: return "\033[38;2;158;123;176m";
        case TermColor.Gray:   return "\033[38;2;106;135;89m";
        case TermColor.Green:  return "\033[38;2;165;194;97m";
        case TermColor.Brown:  return "\033[38;2;204;130;66m";
        default: return "\033[0m";
    }
}

void print(TermColor C = TermColor.Gray, T...)(T t)
{
    debug stderr.write(C.termStr, t, TermColor.Default.termStr);
}
void printf(TermColor C = TermColor.Gray, T...)(T t)
{
    debug stderr.write(C.termStr);
    debug stderr.writef(t);
    debug stderr.write(TermColor.Default.termStr);
}
void println(TermColor C = TermColor.Gray, T...)(T t)
{
    debug stderr.writeln(C.termStr, t, TermColor.Default.termStr);
}
void printfln(TermColor C = TermColor.Gray, T...)(T t)
{
    debug stderr.write(C.termStr);
    debug stderr.writefln(t);
    debug stderr.write(TermColor.Default.termStr);
}

// debug{
//     void write(T...)(T t)
//     {
//         stdout.write(TermColor.Blue.termStr, t, TermColor.Default.termStr);
//     }
//     void writeln(T...)(T t)
//     {
//         stdout.writeln(TermColor.Blue.termStr, t, TermColor.Default.termStr);
//     }
//     void writefln(T...)(T t)
//     {
//         stdout.writefln(TermColor.Blue.termStr, t, TermColor.Default.termStr);
//     }
// }

T[] scanArray(T = long)()
{
    return readln.split.to!(T[]);
}

char scanChar()
{
    int c = ' ';
    while (isWhite(c) && c != -1)
    {
        c = getchar;
    }
    return cast(char)c;
}
T scanElem(T = long)()
{
    char[] res;
    int c = ' ';
    while (isWhite(c) && c != -1)
    {
        c = getchar;
    }
    while (!isWhite(c) && c != -1)
    {
        res ~= cast(char) c;
        c = getchar;
    }
    return res.strip.to!T;
}

alias scanString = scanElem!string;

template fold(fun...) if (fun.length >= 1)
{
    auto fold(R, S...)(R r, S seed)
    {
        static if (S.length < 2)
        {
            return reduce!fun(seed, r);
        }
        else
        {
            import std.typecons : tuple;

            return reduce!fun(tuple(seed), r);
        }
    }
}

struct Factor
{
    long n;
    long c;
}

//素因数分解
Factor[] factors(long n) pure
{
    Factor[] res;
    for (long i = 2; i ^^ 2 <= n; i++)
    {
        if (n % i != 0)
            continue;

        long c;
        while (n % i == 0)
        {
            n = n / i;
            c++;
        }
        res ~= Factor(i, c);
    }
    if (n != 1)
        res ~= Factor(n, 1);

    return res;
}
//約数をすべて列挙
long[] divisors(long n) pure
{
    long[] list;
    void func(Factor[] fs, long n)
    {
        if(fs.empty){
            list ~= n;
            return;
        }

        foreach(c; 0..fs[0].c+1)
        {
            func(fs[1..$], n * (fs[0].n ^^ c));
        }
    }

    func(factors(n), 1);
    sort(list);
    return list;
}
//nまでの素数のリスト
long[] primes(long n) pure
{
    if(n<2)return [];

    auto table = new long[cast(size_t)n+1];

    long[] res;
    for(size_t i = 2;i<=n;i++)
    {
        if(table[i]==-1) continue;
        for(size_t a = i;a<table.length;a+=i)
        {
            table[a] = -1;
        }
        res ~= i;
    }
    return res;
}
//素数判定
bool isPrime(long n) pure
{
    if (n <= 1)
        return false;
    if (n == 2)
        return true;
    if (n % 2 == 0)
        return false;
    for (long i = 3; i ^^ 2 <= n; i += 2)
        if (n % i == 0)
            return false;
    return true;
}


Measured memory_bytes: 109948