#pragma region
#include <bits/stdc++.h>
using namespace std;

#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;

#define int long long
#define REP(i,n) for(int i = 0;i < (int)(n);i++)
#define RREP(i,n) for(int i = (int)n-1;i >= 0;i--)
#define FOR(i,s,n) for(int i = s;i < (int)n;i++)
#define RFOR(i,s,n) for(int i = (int)n-1;i >= s;i--)
#define ALL(a) a.begin(),a.end()
#define IN(a, x, b) (a<=x && x<b)
template<class T>inline bool CHMAX(T&a,T b){if(a<b){a = b;return true;}return false;}
template<class T>inline bool CHMIN(T&a,T b){if(a>b){a = b;return true;}return false;}
template<class T>istream&operator >>(istream&is,vector<T>&vec){for(T&x:vec)is>>x;return is;}
template<class T>inline void in(T&t){cin>>t;}
template<class T,class... Ts>inline void in(T&t,Ts&...ts){cin>>t;in(ts...);}
template<class T>inline void out(T t){cout<<t<<"\n";}
template<class T,class... Ts>inline void out(T t,Ts... ts){cout<<t<<" ";out(ts...);}
template<typename T=int>vector<T>mv(size_t a){return vector<T>(a);}
template<typename T=int,typename... Ts>auto mv(size_t a,Ts... ts){return vector<decltype(mv<T>(ts...))>(a,mv<T>(ts...));}
template<typename T,typename V>typename enable_if<is_class<T>::value==0>::type fill(T&t,const V&v){t=v;}
template<typename T,typename V>typename enable_if<is_class<T>::value!=0>::type fill(T&t,const V&v){for(auto &e:t)fill(e,v);}
constexpr long long INF = 1e18;
 
template<int MOD> struct Fp {
	long long val;
	constexpr Fp(long long v = 0) noexcept : val(v % MOD) {
		if (val < 0) val += MOD;
	}
	constexpr int getmod() { return MOD; }
	constexpr Fp operator - () const noexcept {
		return val ? MOD - val : 0;
	}
	constexpr Fp operator + (const Fp& r) const noexcept { return Fp(*this) += r; }
	constexpr Fp operator - (const Fp& r) const noexcept { return Fp(*this) -= r; }
	constexpr Fp operator * (const Fp& r) const noexcept { return Fp(*this) *= r; }
	constexpr Fp operator / (const Fp& r) const noexcept { return Fp(*this) /= r; }
	constexpr Fp& operator += (const Fp& r) noexcept {
		val += r.val;
		if (val >= MOD) val -= MOD;
		return *this;
	}
	constexpr Fp& operator -= (const Fp& r) noexcept {
		val -= r.val;
		if (val < 0) val += MOD;
		return *this;
	}
	constexpr Fp& operator *= (const Fp& r) noexcept {
		val = val * r.val % MOD;
		return *this;
	}
	constexpr Fp& operator /= (const Fp& r) noexcept {
		long long a = r.val, b = MOD, u = 1, v = 0;
		while (b) {
			long long t = a / b;
			a -= t * b; swap(a, b);
			u -= t * v; swap(u, v);
		}
		val = val * u % MOD;
		if (val < 0) val += MOD;
		return *this;
	}
	constexpr bool operator == (const Fp& r) const noexcept {
		return this->val == r.val;
	}
	constexpr bool operator != (const Fp& r) const noexcept {
		return this->val != r.val;
	}
	friend constexpr ostream& operator << (ostream &os, const Fp<MOD>& x) noexcept {
		return os << x.val;
	}
	friend constexpr Fp<MOD> modpow(const Fp<MOD> &a, long long n) noexcept {
		if (n == 0) return 1;
		auto t = modpow(a, n / 2);
		t = t * t;
		if (n & 1) t = t * a;
		return t;
	}
};
 
// 二項係数ライブラリ
template<class T> struct BiCoef {
	vector<T> fact_, inv_, finv_;
	constexpr BiCoef() {}
	constexpr BiCoef(int n) noexcept : fact_(n, 1), inv_(n, 1), finv_(n, 1) {
		init(n);
	}
	constexpr void init(int n) noexcept {
		fact_.assign(n, 1), inv_.assign(n, 1), finv_.assign(n, 1);
		int MOD = fact_[0].getmod();
		for(int i = 2; i < n; i++){
			fact_[i] = fact_[i-1] * i;
			inv_[i] = -inv_[MOD%i] * (MOD/i);
			finv_[i] = finv_[i-1] * inv_[i];
		}
	}
	constexpr T com(int n, int k) const noexcept {
		if (n < k || n < 0 || k < 0) return 0;
		return fact_[n] * finv_[k] * finv_[n-k];
	}
	constexpr T fact(int n) const noexcept {
		if (n < 0) return 0;
		return fact_[n];
	}
	constexpr T inv(int n) const noexcept {
		if (n < 0) return 0;
		return inv_[n];
	}
	constexpr T finv(int n) const noexcept {
		if (n < 0) return 0;
		return finv_[n];
	}
};
 
//const int MOD = 1000000007;
//const int MOD = 998244353;
//using mint = Fp<MOD>;
//BiCoef<mint> bc;
// bc.init(500050);
#pragma endregion
 
template< typename T >
struct edge {
  int src, to;
  T cost;
 
  edge(int to, T cost) : src(-1), to(to), cost(cost) {}
 
  edge(int src, int to, T cost) : src(src), to(to), cost(cost) {}
 
  edge &operator=(const int &x) {
	to = x;
	return *this;
  }
 
  operator int() const { return to; }
};
template< typename T >
using Edges = vector< edge< T > >;
template< typename T >
using WeightedGraph = vector< Edges< T > >;
using UnWeightedGraph = vector< vector< int > >;
template< typename T >
using Matrix = vector< vector< T > >;
template< typename G >
struct DoublingLowestCommonAncestor {
  const int LOG;
  vector< int > dep;
  const G &g;
  vector< vector< int > > table;
 
  DoublingLowestCommonAncestor(const G &g) : g(g), dep(g.size()), LOG(32 - __builtin_clz(g.size())) {
	table.assign(LOG, vector< int >(g.size(), -1));
  }
 
  void dfs(int idx, int par, int d) {
	table[0][idx] = par;
	dep[idx] = d;
	for(auto &to : g[idx]) {
	  if(to != par) dfs(to, idx, d + 1);
	}
  }
 
  void build() {
	dfs(0, -1, 0);// ここを書き換えれば親を選択できる
	for(int k = 0; k + 1 < LOG; k++) {
	  for(int i = 0; i < table[k].size(); i++) {
		if(table[k][i] == -1) table[k + 1][i] = -1;
		else table[k + 1][i] = table[k][table[k][i]];
	  }
	}
  }
 
  int query(int u, int v) {
	if(dep[u] > dep[v]) swap(u, v);
	for(int i = LOG - 1; i >= 0; i--) {
	  if(((dep[v] - dep[u]) >> i) & 1) v = table[i][v];
	}
	if(u == v) return u;
	for(int i = LOG - 1; i >= 0; i--) {
	  if(table[i][u] != table[i][v]) {
		u = table[i][u];
		v = table[i][v];
	  }
	}
	return table[0][u];
  }
};

template<typename T>
using pbds = tree<T,null_type,less<T>,rb_tree_tag,tree_order_statistics_node_update>;

/*
insert(logN)
erase(logN)
find_by_order(logN)//k番目の要素のイテレータを返す
order_of_key(logN)//k以上で最初の数が何番目かを返す
(K未満の個数が何個あるかが分かるらしい）
gp_hash_table<int,int>m//高速なmap
*/
 
struct SegmentTree{
	int n;
	pair<int,int> ti = pair<int,int>(0,0);
	vector<gp_hash_table<int,pair<int,int>>> dat;
	SegmentTree(){};
	void init(int n_){
		n=1;
		while(n<n_) n<<=1;
		dat.resize(n<<1);
	}
	void build(int N){
		int n_=N;
		init(n_);
	}
	pair<int,int> f(pair<int,int> a,pair<int,int> b){
		return {a.first+b.first,a.second+b.second};
	}
	void set_val(int k,pair<int,int> x,int col){
		dat[k+=n][col]=x;
		while(k>>=1)
			dat[k][col]=f(dat[(k<<1)|0][col],dat[(k<<1)|1][col]);
	}
	pair<int,int> query(int a,int b,int col){
		pair<int,int> vl=ti,vr=ti;
		for(int l=a+n,r=b+n;l<r;l>>=1,r>>=1) {
			if(l&1) vl=f(vl,dat[l++][col]);
			if(r&1) vr=f(dat[--r][col],vr);
		}
		return f(vl,vr);
	}
};
//auto f = [](){};
//auto seg = SegmentTree<T,decltype(f)>(f,Ti);
 
signed main(){
	int N,Q;
	in(N,Q);
	WeightedGraph<int>g(N);
	auto c = mv(N-1),d = c;
	REP(i,N-1){
		int a,b;
		in(a,b,c[i],d[i]);
		--a;--b;--c[i];
		g[a].emplace_back(b,i);
		g[b].emplace_back(a,i);
	}
	DoublingLowestCommonAncestor<WeightedGraph<int>>lca(g);
	lca.build();
	auto ds = mv(N),us = ds;
	fill(ds,-1);
	fill(us,-1);
	SegmentTree seg;
	seg.build(2*N);
	int idx = 0;
	vector<int>sum;
	sum.push_back(0);
	auto dfs = [&](auto&&f,int now,int par)->void{
		for(auto e:g[now]){
			if(e == par)continue;
			ds[e] = idx;
			seg.set_val(idx,{d[e.cost],1},c[e.cost]);
			sum.push_back(d[e.cost]);
			++idx;
			f(f,e,now);
			us[e] = idx;
			seg.set_val(idx,{-d[e.cost],-1},c[e.cost]);
			sum.push_back(-d[e.cost]);
			++idx;
		}
	};
	dfs(dfs,0,-1);
	REP(i,sum.size()-1)sum[i+1]+=sum[i];
	vector<int>ans;
	while(Q--){
		int x,y,a,b;
		in(x,y,a,b);
		x--;a--;b--;
		int p = lca.query(a,b);
		int ret = 0;
		auto f = [&](int child){
			int l = ds[p]+1,r = ds[child]+1;
			int res = sum[r]-sum[l];
			auto p = seg.query(l,r,x);
			res -= p.first;
			res += p.second * y;
			return res;
		};
		ret+=f(a);
		ret+=f(b);
		ans.push_back(ret);
	}
	REP(i,ans.size()){
		out(ans[i]);
	}
}

Measured memory_bytes: 623868