#include <bits/stdc++.h>
#define rep(i,n) for(int i=0;i<(int)(n);i++)
#define rep1(i,n) for(int i=1;i<=(int)(n);i++)
#define all(c) c.begin(),c.end()
#define pb push_back
#define fs first
#define sc second
#define chmin(x,y) x=min(x,y)
#define chmax(x,y) x=max(x,y)
using namespace std;
template<class S,class T> ostream& operator<<(ostream& o,const pair<S,T> &p){
	return o<<"("<<p.fs<<","<<p.sc<<")";
}
template<class T> ostream& operator<<(ostream& o,const vector<T> &vc){
	o<<"{";
	for(const T& v:vc) o<<v<<",";
	o<<"}";
	return o;
}
using ll = long long;
template<class T> using V = vector<T>;
template<class T> using VV = vector<vector<T>>;
constexpr ll TEN(int n) { return (n == 0) ? 1 : 10 * TEN(n-1); }

#ifdef LOCAL
#define show(x) cerr << "LINE" << __LINE__ << " : " << #x << " = " << (x) << endl
#else
#define show(x) true
#endif

template<class Handler>
struct segtree_lazy{
	using val_t = typename Handler::val_t;
	using opr_t = typename Handler::opr_t;
	int N;
	vector<val_t> val;
	vector<opr_t> lazy;
	segtree_lazy(){}
	segtree_lazy(int n){init(n);}
	segtree_lazy(const vector<val_t>& vc){init(vc);}
	void init(int n){
		N=1;
		while(N<n) N*=2;
		val .assign(N*2,val_t::e());
		lazy.assign(N*2,opr_t::e());
	}
	void init(const vector<val_t>& vc){
		int n = vc.size();
		N=1;
		while(N<n) N*=2;
		val .assign(N*2,val_t::e());
		rep(i,n) val[i+N] = vc[i];
		for(int i=N-1;i>0;i--) val[i] = val[i*2] + val[i*2+1];
		lazy.assign(N*2,opr_t::e());
	}
	val_t realvalue(int k,int l,int r){
//		return Handler::act(lazy[k],val[k]);
		return Handler::act(lazy[k],val[k],k,l,r);
	}

	val_t query(int a,int b,int l=0,int r=-1,int k=1){	//query_calc
//		if(k==1) printf("query [%d,%d)\n",a,b);
		if(r==-1) r=N;
		if(b<=l||r<=a) return val_t::e();
		if(a<=l&&r<=b) return realvalue(k,l,r);
		propagate(l,r,k);
		val_t ret = query(a,b,l,(l+r)/2,k*2) + query(a,b,(l+r)/2,r,k*2+1);
		val[k] = realvalue(k*2,l,(l+r)/2) + realvalue(k*2+1,(l+r)/2,r);
		return ret;

	}
//	val_t query_leftassoc(){}
	void update(int a,int b,const opr_t &x,int l=0,int r=-1,int k=1){	//query_update
//		if(k==1) printf("update [%d,%d)\n",a,b);
		if(r==-1) r=N;
		if(b<=l||r<=a) return;
		if(a<=l&&r<=b){
			Handler::setg2fg(x,lazy[k]);
			return;
		}
		propagate(l,r,k);
		update(a,b,x,l,(l+r)/2,k*2);
		update(a,b,x,(l+r)/2,r,k*2+1);
		val[k] = realvalue(k*2,l,(l+r)/2) + realvalue(k*2+1,(l+r)/2,r);
	}
	void propagate(int l,int r,int k){	//opr_child -> opr_parent * opr_child		parent after child
		Handler::setg2fg(lazy[k],lazy[k*2  ]);
		Handler::setg2fg(lazy[k],lazy[k*2+1]);
		lazy[k] = opr_t::e();
	}
};

template<class E, class Handler>		//for lazy segtree
struct HLdecomp{
	using val_t = typename Handler::val_t;
	using opr_t = typename Handler::opr_t;
	using segtree = segtree_lazy<Handler>;

	static const bool OnEdge = 1;

	vector<vector<E>> G;
	using P = pair<int,int>;
	vector<P> id;				//id[v] = P(chain_id,pos_on_chain)	posはtop to bottom
	vector<int> sz;				//sz[v] = subtree_size
	vector<int> dep;			//dep[v] = depth
	vector<int> par;			//par[v] = parent
	vector<int> top;			//id[v].fs の一番上

	int C;	//num of chains
	vector<vector<int>> chains;	//chains[c][i] = v <=> id[v] = P(c,i)
	vector<segtree> segs;

	HLdecomp(const vector<vector<E>>& G, int r = 0):G(G){
		int N = G.size();
		C = 1;
		chains.pb({0});
		id.assign(N,P(0,0));
		sz.assign(N,0);
		dep.assign(N,0);
		par.assign(N,0);
		top.assign(N,0);

		dfs_base(r);
		dfs_decomp(r);

	
		rep(c,C) segs.pb(segtree(chains[c].size()));
	}
	void dfs_base(int v,int p=-1){	//calc sz,dep
		sz[v] = 1;
		par[v] = p;
		if(p==-1) dep[v] = 0;
		else dep[v] = dep[p] + 1;
		for(const auto& e: G[v]){
			int u = e.to;
			if(u==p) continue;
			dfs_base(u,v);
			sz[v] += sz[u];
		}
	}
	void dfs_decomp(int v,int p=-1){
		int heavy = -1;
		for(const auto& e: G[v]){
			int u = e.to;
			if(u == p) continue;
			if(heavy == -1 || sz[heavy]<sz[u]) heavy = u;
		}
		for(const auto& e: G[v]){
			int u = e.to;
			if(u == p) continue;
			if(u == heavy){
				id[u] = P(id[v].fs,id[v].sc+1);
				top[u] = top[v];
			}else{
				id[u] = P(C,0);
				top[u] = u;
				C++;
				chains.pb({});
			}
			chains[id[u].fs].pb(u);
			dfs_decomp(u,v);
		}
	}
	int lca(int a,int b){
		while(id[a].fs != id[b].fs){
			if(id[a].fs < id[b].fs) b = par[top[b]];
			else a = par[top[a]];
		}
		return dep[a]<dep[b] ? a : b;
	}
	int up(int v,int d){	//vから上にd上がった点
		if(dep[v]<d) return -1;
		while(true){
			int d1 = dep[v] - dep[top[v]];
			if(d1<=d){
				d -= d1;
				v = top[v];
			}else{
				int c = id[v].fs;
				return chains[c][d1-d];
			}
			if(d==0) return v;
			v = par[v];
			d--;
		}
		assert(0);
		return -1;
	}
	val_t query(int u,int v){	//[u,w) + [w,v]			edge ver なら [u,w) + (w,v]
		int w = lca(u,v);
		val_t up,down;

		{
			for(;id[u].fs!=id[w].fs; u = par[top[u]]){
				//chains[c][l,r]
				int c = id[u].fs;
				int l = id[top[u]].sc;
				int r = id[u].sc;
				val_t x = segs[c].query(l,r+1);
				up = x + up;
			}
			int c = id[u].fs;
			int l = id[w].sc+1;
			int r = id[u].sc;
			val_t x = segs[c].query(l,r+1);
			up = x + up;
		}
		{
			u = v;
			for(;id[u].fs!=id[w].fs; u = par[top[u]]){
				int c = id[u].fs;
				int l = id[top[u]].sc;
				int r = id[u].sc;
				val_t x = segs[c].query(l,r+1);
				down = x + down;
			}
			int c = id[u].fs;
			int l = id[w].sc + OnEdge;
			int r = id[u].sc;
			val_t x = segs[c].query(l,r+1);
			down = x + down;
		}

		// ! reverse up !
		return up + down;
	}
	void update(int u,int v,const opr_t &x){	//[u,w) + [w,v]
		int w = lca(u,v);

		{
			for(;id[u].fs!=id[w].fs; u = par[top[u]]){
				//chains[c][l,r]
				int c = id[u].fs;
				int l = id[top[u]].sc;
				int r = id[u].sc;
				segs[c].update(l,r+1,x);
			}
			int c = id[u].fs;
			int l = id[w].sc+1;
			int r = id[u].sc;
			segs[c].update(l,r+1,x);
		}
		{
			u = v;
			for(;id[u].fs!=id[w].fs; u = par[top[u]]){
				int c = id[u].fs;
				int l = id[top[u]].sc;
				int r = id[u].sc;
				segs[c].update(l,r+1,x);
			}
			int c = id[u].fs;
			int l = id[w].sc + OnEdge;
			int r = id[u].sc;
			segs[c].update(l,r+1,x);
		}
	}
};

struct handler{
	struct val_t{
		int d;
		val_t(){*this = e();}
		val_t(int d_):d(d_){}

		const static val_t e(){
			return val_t(0);
		}
		val_t operator+(const val_t &r) const {
			return val_t(d+r.d);
		}
	};

	struct opr_t{
		int d;
		opr_t(){*this = e();}
		opr_t(int d_):d(d_){}

		const static opr_t e(){
			return opr_t(0);
		}
	};

	/*
		もしコピーコストとかが気になって,しかも楽に書けるならsetg2fgを直接書く
		そうじゃないなら g = getfg(f,g)
	*/
	static void setg2fg(const opr_t &f, opr_t &g){	//g -> fg		f after g
		g.d += f.d;
	}
	static val_t act(const opr_t &f, const val_t &v,int k,int l,int r){	//assign f.x -> sum = 
		return val_t(f.d+v.d);
	}
};


int main(){
	cin.tie(0);
	ios::sync_with_stdio(false);		//DON'T USE scanf/printf/puts !!
	cout << fixed << setprecision(20);
	
	int N,Q;
	cin >> N >> Q;
	struct E{
		int from,to,c,d;
		E(int from_,int to_,int c_,int d_):from(from_),to(to_),c(c_),d(d_){}
	};
	VV<E> G(N);
	V<E> es;
	rep(i,N-1){
		int a,b,c,d;
		cin >> a >> b >> c >> d;
		a--,b--;
		G[a].emplace_back(a,b,c,d);
		G[b].emplace_back(b,a,c,d);
		es.emplace_back(a,b,c,d);
	}
	HLdecomp<E,handler> HLD(G);

	V<int> x(Q),y(Q),u(Q),v(Q);
	VV<int> c2qs(N);
	VV<E> c2es(N);
	rep(q,Q){
		cin >> x[q] >> y[q] >> u[q] >> v[q];
		u[q]--,v[q]--;
		c2qs[x[q]].pb(q);
	}
	for(auto e: es){
		c2es[e.c].pb(e);
	}

	for(auto e: es){
		HLD.update(e.from,e.to,handler::opr_t(e.d));
	}
	V<int> ans(Q);
	rep(c,N){
		show(c);
		for(auto e: c2es[c]){
			show(e.from);
			show(e.to);
			HLD.update(e.from,e.to,handler::opr_t(-e.d));
			show(e.from);
			show(e.to);
		}
		V<int> w0,w1;
		for(int q: c2qs[c]){
			show(q);
			show(u[q]);
			show(v[q]);
			w0.pb(HLD.query(u[q],v[q]).d);
			show(q);
		}
		show(w0);
		for(auto e: c2es[c]){
			HLD.update(e.from,e.to,handler::opr_t(1));
		}
		for(int q: c2qs[c]){
			w1.pb(HLD.query(u[q],v[q]).d);
		}
		show(w1);
		for(auto e: c2es[c]){
			HLD.update(e.from,e.to,handler::opr_t(e.d-1));
		}
		rep(i,c2qs[c].size()){
			int q = c2qs[c][i];
			ans[q] = w0[i] + (w1[i]-w0[i]) * y[q];
		}
	}
	rep(q,Q){
		cout << ans[q] << endl;
	}
}


Measured memory_bytes: 47972