Problem:
Charging System for Network
There is a network consisting of
N
machines (sequentially numbered from 0 to
N
-1) interconnected through
N
-1 bidirectional communication cables. Any two machines can perform bidirectional communication through one or more cables. From time to time, machines in the network are renewed. When a new machine is introduced, the cables directly connected to it are also replaced with thicker ones to cope with the increased volume of communication traffic.
The communication fee arising from communication traffic between any two machines is calculated by summing the charges assigned to all the cables routing via the two. A unique charging scheme is employed in this system: if the size of a cable is a multiple of
K
, then the cable is not charged (free of charge). Other cables are charged according to their sizes.
Based on the given information on the network topology and
Q
instructions, write a program to execute each instruction.
Increase the sizes of all cables directly connected to the machine
x
by
d
.
Report the communication charge between the machines
s
and
t
.
Input
The input is given in the following format.
N
K
a_1
b_1
c_1
a_2
b_2
c_2
:
a_{N−1}
b_{N−1}
c_{N−1}
Q
query_1
query_2
:
query_Q
The first line provides the number of machines
N
(2 ≤
N
≤ 10
5
) and the cable size
K
(1 ≤
K
≤ 10
5
) (the reference value for determining free of charge cables). Each of subsequent
N
-1 lines provides the
i
-th cable information that directly connects two machines
a_i
and
b_i
(0 ≤
a_i
<
b_i
≤
N
-1), followed by the cable’s initial size
c_i
(1 ≤
c_i
≤ 10
5
). For any pair of machines, the number of cables directly connecting them is either one or zero. The next line following them provides the number of instructions
Q
(1 ≤
Q
≤ 10
5
). Each of the Q lines following it provides the
i
-th instruction
query_i
, which is either one of the following two:
add
x
d
or
send
s
t
The instruction
add
x
d
increase the size of all cables directly connected to machine
x
(0 ≤
x
≤
N
-1) by
d
(1 ≤
d
≤ 10
5
).
The instruction
send
s
t
reports the charge imposed to the communication between the two machines
s
(0 ≤
s
≤
N
-1) and
t
(0 ≤
t
≤
N
-1), where
s ≠ t
.
At least one
send
instruction is included in the input information.
Output
For each
send
command, output the communication charge between
s
and
t
.
Sample Input 1
6 3
0 1 1
0 2 1
0 3 1
2 4 1
2 5 1
3
send 1 4
add 2 2
send 1 4
Sample Output 1
3
1

Submitted solutions:

code_snippet: // AOJ #0367 Charging System for Network
// 2025.4.26

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int NMAX = 200000;

#define gc() getchar_unlocked()
#define pc(c) putchar_unlocked(c)

int Cin() {
	int n = 0; int c = gc();
	do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
	return n;
}

string Cins() {
    char temp[10];
    char *s = temp;
    do *s = gc();
    while (*s++ > ' ');
    *(s-1) = 0;
    string input(temp);
    return input;
}

void Cout(ll n) {
	char b[30];
	if (!n) pc('0');
	else {
		if (n < 0) pc('-'), n = -n;
		int i = 0; while (n) b[i++] = n % 10 + '0', n /= 10;
		while (i--) pc(b[i]);
	}
	pc('\n');
}

int K;
int a[NMAX], b[NMAX], c[NMAX];
vector<int> g[NMAX];
ll add[NMAX], sum[NMAX], baseCost[NMAX];

struct SegTree {
    int size;
    vector<ll> data;

    SegTree() {}
    SegTree(int n) {
        size = 1;
        while (size < n) size <<= 1;
        data.assign(2 * size, 0);
    }

    void update(int pos, ll val) {
        pos += size;
        data[pos] = val;
        for (pos >>= 1; pos > 0; pos >>= 1)
            data[pos] = data[pos * 2] + data[pos * 2 + 1];
    }

    ll query(int l, int r) {
        ll res = 0;
        for (l += size, r += size; l < r; l >>= 1, r >>= 1) {
            if (l & 1) res += data[l++];
            if (r & 1) res += data[--r];
        }
        return res;
    }
} seg;

namespace HLD {
    int par[NMAX], sz[NMAX], ch[NMAX], vid[NMAX], heavy[NMAX], depth[NMAX];
    int curr_vid = 0;

    void dfs(int node, int parent, int d) {
        par[node] = parent;
        sz[node] = 1;
        depth[node] = d;
        for (int &to : g[node]) {
            if (to == parent) continue;
            dfs(to, node, d + 1);
            sz[node] += sz[to];
        }
    }

    void decompose(int node, int h) {
        ch[node] = h;
        vid[node] = curr_vid++;
        int heavy_child = -1, max_sz = 0;
        for (int to : g[node]) {
            if (to == par[node]) continue;
            if (sz[to] > max_sz) {
                max_sz = sz[to];
                heavy_child = to;
            }
        }
        if (heavy_child != -1) {
            heavy[node] = heavy_child;
            decompose(heavy_child, h);
        } else heavy[node] = -1;
        for (int to : g[node])
            if (to != par[node] && to != heavy_child) decompose(to, to);
    }

    ll path_query(int u, int v) {
        ll res = 0;
        while (true) {
            if (ch[u] == ch[v]) {
                if (vid[u] > vid[v]) swap(u, v);
                res += seg.query(vid[u] + 1, vid[v] + 1);
                return res;
            }
            if (depth[ch[u]] < depth[ch[v]]) swap(u, v);
            res += seg.query(vid[ch[u]] + 1, vid[u] + 1);
            ll extra = baseCost[ch[u]] + add[ch[u]] + (par[ch[u]] != -1 ? add[par[ch[u]]] : 0);
            if (extra % K) res += extra;
            u = par[ch[u]];
        }
    }
}

int main() {
    int n = Cin();
    K = Cin();
    for (int i = 0; i < n - 1; ++i) {
        a[i] = Cin(), b[i] = Cin(), c[i] = Cin();
        g[a[i]].push_back(b[i]);
        g[b[i]].push_back(a[i]);
    }

    HLD::dfs(0, -1, 0);
    HLD::decompose(0, 0);

    seg = SegTree(n);
    for (int i = 0; i < n - 1; ++i) {
        if (HLD::par[b[i]] != a[i]) swap(a[i], b[i]);
        sum[b[i]] = c[i];
        baseCost[b[i]] = c[i];
        seg.update(HLD::vid[b[i]], (c[i] % K ? c[i] : 0));
    }

    int q = Cin();
    string cmd;
    while (q--) {
        cmd = Cins();
        if (cmd[0] == 'a') {
            int x = Cin(), d = Cin();
            add[x] += d, sum[x] += d;
            seg.update(HLD::vid[x], (sum[x] % K ? sum[x] : 0));
            int y = HLD::heavy[x];
            if (y != -1) {
                sum[y] += d;
                seg.update(HLD::vid[y], (sum[y] % K ? sum[y] : 0));
            }
        } else {
            int s = Cin(), t = Cin();
            Cout(HLD::path_query(s, t));
        }
    }
    return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 29796
final_rank: 1

code_snippet: #include <bits/stdc++.h>
using namespace std;

struct HLD {
	vector<vector<long long>> g;
	vector<long long> p,s,v,d,e,segt,sum;
	vector<pair<long long,long long>> a,ran;
	long long n,K;
	HLD(vector<vector<long long>> g,long long k) : n(g.size()), g(g), K(k) {
		p.resize(n,-2ll);
		s.resize(n,0ll);
		a.resize(n);
		d.resize(n,-1ll);
		e.resize(n,-1ll);
		segt.resize(n,0ll);
		sum.resize(n,0ll);
		{
			p[0ll] = -1ll;
			d[0ll] = 0ll;
			queue<long long> q;
			q.push(0ll);
			while(!q.empty()){
				long long it = q.front();
				q.pop();
				for(long long i : g[it]){
					if(p[i] != -2ll) continue;
					p[i] = it;
					d[i] = d[it] + 1ll;
					q.push(i);
				}
			}
		}
		siz(0ll);
		ran.push_back({0ll,-1ll});
		dfs(0ll,0ll);
	}
	long long siz(long long x){
		if(s[x] != 0ll) return s[x];
		s[x] = 1ll;
		for(long long i : g[x]){
			if(i == p[x]) continue;
			s[x] += siz(i);
		}
		return s[x];
	}
	void dfs(long long x,long long r){
		a[x] = {(long long)v.size(),r};
		v.push_back(x);
		long long ma = -1ll;
		ran.back().second++;
		for(long long i : g[x]){
			if(i == p[x]) continue;
			if(ma == -1ll || s[ma] < s[i]){
				ma = i;
			}
		}
		if(ma == -1ll) return;
		dfs(ma,r);
		e[x] = ma;
		for(long long i : g[x]){
			if(i == p[x] || i == ma) continue;
			ran.push_back({v.size(),v.size() - 1ll});
			dfs(i,i);
		}
	}
	vector<pair<long long,long long>> get(long long u,long long v){
		vector<pair<long long,long long>> res;
		long long cou = 0ll;
		while(a[u].second != a[v].second){
			if(d[a[u].second] <= d[a[v].second]){
				res.push_back({a[a[v].second].first,a[v].first});
				long long it = segt[a[a[v].second].first];
				it += sum[a[a[v].second].first];
				it += sum[a[p[a[v].second]].first];
				if(it % K != 0ll){
					cou += it;
				}
				v = p[a[v].second];
			}else{
				res.push_back({a[a[u].second].first,a[u].first});
				long long it = segt[a[a[u].second].first];
				it += sum[a[a[u].second].first];
				it += sum[a[p[a[u].second]].first];
				if(it % K != 0ll){
					cou += it;
				}
				u = p[a[u].second];
			}
		}
		res.push_back({min(a[u].first,a[v].first),max(a[u].first,a[v].first)});
		res.push_back({-1ll,cou});
		return res;
	}
	long long operator[](long long x){
		return a[x].first;
	}
	long long lca(long long u,long long v){
		return this->v[get(u,v).back().first];
	}
	long long depth(long long x){
		return d[x];
	}
	long long size(){
		return ran.size();
	}
	long long dist(long long u,long long v){
		long long lc = lca(u,v);
		return depth(u) + depth(v) - depth(lc) * 2ll;
	}
};
template<typename T,T e>
struct segtree {
	vector<long long> v;
	long long n;
	T f(T a,T b){
		return a + b;
	}
	segtree(long long n) : n(n) {
		v.resize(n * 2ll);
		for(long long i = 0ll;i < n * 2ll;i++){
			v[i] = e;
		}
	}
	segtree(vector<T> x) : n(x.size()) {
		v.resize(n * 2ll);
		for(long long i = 0ll;i < n;i++){
			v[i + n] = x[i];
		}
		for(long long i = n - 1ll;i >= 0ll;i--){
			v[i] = f(v[i * 2ll],v[i * 2ll + 1ll]);
		}
	}
	T operator[](long long a){
		return v[a + n];
	}
	void set(long long a,long long b){
		a += n;
		v[a] = b;
		while(a >>= 1ll){
			v[a] = f(v[a * 2ll],v[a * 2ll + 1ll]);
		}
	}
	T prod(long long l,long long r){
		T res = e;
		for(l += n,r += n;l < r;l >>= 1ll,r >>= 1ll){
			if(l & 1ll){
				res = f(res,v[l++]);
			}
			if(r & 1ll){
				res = f(res,v[--r]);
			}
		}
		return res;
	}
};
int main()
{
	long long n,k;
	cin >> n >> k;
	vector<vector<pair<long long,long long>>> g(n,vector<pair<long long,long long>>());
	vector<vector<long long>> gh(n,vector<long long>());
	for(long long i = 0ll;i < n - 1ll;i++){
		long long u,v,w;
		cin >> u >> v >> w;
		gh[u].push_back(v);
		gh[v].push_back(u);
		g[u].push_back({v,w});
		g[v].push_back({u,w});
	}
	HLD hld(gh,k);
	vector<long long> v = hld.v;
	vector<pair<long long,long long>> a = hld.a;
	for(long long i = 0ll;i < n;i++){
		for(pair<long long,long long> j : g[i]){
			if(j.first < i) continue;
			if(hld.p[i] == j.first){
				hld.segt[a[i].first] = j.second;
			}else{
				hld.segt[a[j.first].first] = j.second;
			}
		}
	}
	segtree<long long,0ll> seg(hld.segt);
	for(long long i = 0ll;i < n;i++){
		if(hld.segt[i] % k == 0ll){
			seg.set(i,0ll);
		}
	}
	long long Q;
	cin >> Q;
	for(long long i = 0ll;i < Q;i++){
		string s;
		cin >> s;
		long long u,v;
		cin >> u >> v;
		if(s == "add"){
			hld.sum[a[u].first] += v;
			if(hld.p[u] != -1ll && a[u].second == a[hld.p[u]].second){
				hld.segt[a[u].first] += v;
				if(hld.segt[a[u].first] % k == 0ll){
					seg.set(a[u].first,0ll);
				}else{
					seg.set(a[u].first,hld.segt[a[u].first]);
				}
			}
			if(hld.e[u] != -1ll){
				hld.segt[a[hld.e[u]].first] += v;
				if(hld.segt[a[hld.e[u]].first] % k == 0ll){
					seg.set(a[hld.e[u]].first,0ll);
				}else{
					seg.set(a[hld.e[u]].first,hld.segt[a[hld.e[u]].first]);
				}
			}
		}else{
			long long ans = 0ll;
			vector<pair<long long,long long>> it = hld.get(u,v);
			for(pair<long long,long long> i : it){
				if(i.first == -1ll){
					ans += i.second;
				}else{
					ans += seg.prod(i.first,i.second + 1ll);
					long long it = seg[i.first];
					ans -= it;
				}
			}
			cout << ans << endl;
		}
	}
}
accuracy: 1
time_ms: 240
memory_kb: 41548
final_rank: 9

code_snippet: #include <bits/stdc++.h>
using namespace std;

struct lca_tree {
  int n, lg;
  vector<int> dep;
  vector<vector<int>> dest;
  lca_tree(vector<vector<int>> tree, vector<int> a, vector<int> b) {
    n = tree.size();
    lg = 0;
    while((1<<lg) < n) lg++;
    dep = vector<int>(n, 0);
    dest = vector(lg+1, vector<int>(n+1, n));
    auto dfs = [&](auto dfs, int cur) -> void {
      for(int e : tree[cur]) {
        int nxt = a[e]^b[e]^cur;
        if(nxt == dest[0][cur]) continue;
        dep[nxt] = dep[cur]+1;
        dest[0][nxt] = cur;
        dfs(dfs, nxt);
      }
    };
    dfs(dfs, 0);
    for(int k = 0; k < lg; k++) {
      for(int i = 0; i < n; i++) {
        dest[k+1][i] = dest[k][dest[k][i]];
      }
    }
  }
  void mov(int &u, int l) {
    for(int k = 0; k <= lg; k++) {
      if((l>>k)&1) {
        u = dest[k][u];
      }
    }
  }
  int operator()(int u, int v) {
    if(dep[u] > dep[v]) swap(u, v);
    mov(v, dep[v]-dep[u]);
    if(u == v) return u;
    for(int k = lg; k >= 0; k--) {
      if(dest[k][u] != dest[k][v]) {
        u = dest[k][u];
        v = dest[k][v];
      }
    }
    return dest[0][u];
  }
};

int main() {
  int N;
  long long K;
  cin >> N >> K;
  const int B = 1000;
  vector<int> a(N-1), b(N-1);
  vector<long long> c(N-1);
  vector<vector<int>> G(N);
  for(int i = 0; i < N-1; i++) {
    cin >> a[i] >> b[i] >> c[i];
    G[a[i]].push_back(i);
    G[b[i]].push_back(i);
  }
  unordered_set<int> naive;
  vector<int> dest(N, -1);
  vector<long long> cost(N), cv(N);
  auto f = [&](int e) -> long long {
    if((c[e]+cv[a[e]]+cv[b[e]])%K == 0) return 0;
    return c[e]+cv[a[e]]+cv[b[e]];
  };
  auto dfs = [&](auto dfs, int cur, int par) -> void {
    for(int &e : G[cur]) {
      int nxt = a[e]^b[e]^cur;
      if(nxt == par) swap(e, G[cur][0]);
    }
    for(int &e : G[cur]) {
      int nxt = a[e]^b[e]^cur;
      if(nxt == par) continue;
      if(!naive.count(cur) && !naive.count(nxt)) {
        dest[nxt] = naive.count(par) ? cur : dest[cur];
        cost[nxt] = cost[cur]+f(e);
      }
      else {
        dest[nxt] = cur;
        cost[nxt] = naive.count(nxt) ? 0 : f(e);
      }
      dfs(dfs, nxt, cur);
    }
  };
  auto mov = [&](auto mov, int u, int v) -> long long {
    if(u == v) {
      return 0;
    }
    else if(naive.count(u)) {
      int e = G[u][0];
      return mov(mov, a[e]^b[e]^u, v)+f(e);
    }
    else if(dest[u] == dest[v]) {
      return cost[u]-cost[v];
    }
    return cost[u]-cost[dest[u]]+mov(mov, dest[u], v);
  };
  int Q;
  cin >> Q;
  lca_tree lca(G, a, b);
  vector<array<int, 3>> query(Q);
  for(int i = 0; i < Q; i++) {
    string op;
    cin >> op >> query[i][1] >> query[i][2];
    query[i][0] = op == "add" ? 1 : 2;
  }
  for(int i = 0; i < Q; i+=B) {
    naive.clear();
    unordered_set<int> x_all;
    for(int j = i; j < min(i+B, Q); j++) {
      if(query[j][0] == 1) {
        int x = query[j][1];
        x_all.insert(x);
      }
    }
    for(const int x : x_all) {
      naive.insert(x);
      for(int e : G[x]) {
        int y = a[e]^b[e]^x;
        naive.insert(y);
      }
    }
    dfs(dfs, 0, -1);
    for(int j = i; j < min(i+B, Q); j++) {
      if(query[j][0] == 1) {
        int x = query[j][1];
        int d = query[j][2];
        cv[x] += d;
      }else {
        int s = query[j][1];
        int t = query[j][2];
        int v = lca(s, t);
        cout << mov(mov, s, v)+mov(mov, t, v) << endl;
      }
    }
  }
}
accuracy: 1
time_ms: 830
memory_kb: 41424
final_rank: 16

code_snippet: #include <bits/stdc++.h>
using namespace std;

struct lca_tree {
  int n, lg;
  vector<int> dep;
  vector<vector<int>> dest;
  lca_tree(vector<vector<int>> tree, vector<int> a, vector<int> b) {
    n = tree.size();
    lg = 0;
    while((1<<lg) < n) lg++;
    dep = vector<int>(n, 0);
    dest = vector(lg+1, vector<int>(n+1, n));
    auto dfs = [&](auto dfs, int cur) -> void {
      for(int e : tree[cur]) {
        int nxt = a[e]^b[e]^cur;
        if(nxt == dest[0][cur]) continue;
        dep[nxt] = dep[cur]+1;
        dest[0][nxt] = cur;
        dfs(dfs, nxt);
      }
    };
    dfs(dfs, 0);
    for(int k = 0; k < lg; k++) {
      for(int i = 0; i < n; i++) {
        dest[k+1][i] = dest[k][dest[k][i]];
      }
    }
  }
  void mov(int &u, int l) {
    for(int k = 0; k <= lg; k++) {
      if((l>>k)&1) {
        u = dest[k][u];
      }
    }
  }
  int operator()(int u, int v) {
    if(dep[u] > dep[v]) swap(u, v);
    mov(v, dep[v]-dep[u]);
    if(u == v) return u;
    for(int k = lg; k >= 0; k--) {
      if(dest[k][u] != dest[k][v]) {
        u = dest[k][u];
        v = dest[k][v];
      }
    }
    return dest[0][u];
  }
};

int main() {
  int N;
  long long K;
  cin >> N >> K;
  const int B = ceil(sqrt(N));
  vector<int> a(N-1), b(N-1);
  vector<long long> c(N-1);
  vector<vector<int>> G(N);
  for(int i = 0; i < N-1; i++) {
    cin >> a[i] >> b[i] >> c[i];
    G[a[i]].push_back(i);
    G[b[i]].push_back(i);
  }
  unordered_set<int> naive;
  vector<int> dest(N, -1);
  vector<long long> cost(N), cv(N);
  auto f = [&](int e) -> long long {
    if((c[e]+cv[a[e]]+cv[b[e]])%K == 0) return 0;
    return c[e]+cv[a[e]]+cv[b[e]];
  };
  auto dfs = [&](auto dfs, int cur, int par) -> void {
    for(int &e : G[cur]) {
      int nxt = a[e]^b[e]^cur;
      if(nxt == par) swap(e, G[cur][0]);
    }
    for(int &e : G[cur]) {
      int nxt = a[e]^b[e]^cur;
      if(nxt == par) continue;
      if(!naive.count(cur) && !naive.count(nxt)) {
        dest[nxt] = naive.count(par) ? cur : dest[cur];
        cost[nxt] = cost[cur]+f(e);
      }
      else {
        dest[nxt] = cur;
        cost[nxt] = naive.count(nxt) ? 0 : f(e);
      }
      dfs(dfs, nxt, cur);
    }
  };
  auto mov = [&](auto mov, int u, int v) -> long long {
    if(u == v) {
      return 0;
    }
    else if(naive.count(u)) {
      int e = G[u][0];
      return mov(mov, a[e]^b[e]^u, v)+f(e);
    }
    else if(dest[u] == dest[v]) {
      return cost[u]-cost[v];
    }
    // cout << u << " : " << cost[u] << " " << dest[u] << endl;
    return cost[u]-cost[dest[u]]+mov(mov, dest[u], v);
  };
  int Q;
  cin >> Q;
  lca_tree lca(G, a, b);
  vector<array<int, 3>> query(Q);
  for(int i = 0; i < Q; i++) {
    string op;
    cin >> op >> query[i][1] >> query[i][2];
    query[i][0] = op == "add" ? 1 : 2;
  }
  for(int i = 0; i < Q; i+=B) {
    naive.clear();
    unordered_set<int> x_all;
    for(int j = i; j < min(i+B, Q); j++) {
      if(query[j][0] == 1) {
        int x = query[j][1];
        x_all.insert(x);
      }
    }
    for(const int x : x_all) {
      naive.insert(x);
      for(int e : G[x]) {
        int y = a[e]^b[e]^x;
        naive.insert(y);
      }
    }
    dfs(dfs, 0, -1);
    for(int j = i; j < min(i+B, Q); j++) {
      if(query[j][0] == 1) {
        int x = query[j][1];
        int d = query[j][2];
        cv[x] += d;
      }else {
        int s = query[j][1];
        int t = query[j][2];
        int v = lca(s, t);
        // int ms = mov(mov, s, v), mt = mov(mov, t, v);
        // cout << ms << " " << mt << endl;
        cout << mov(mov, s, v)+mov(mov, t, v) << endl;
      }
    }
  }
}
accuracy: 1
time_ms: 2050
memory_kb: 41424
final_rank: 18

code_snippet: #include<bits/stdc++.h>
// #include<atcoder/all>
// #include<boost/multiprecision/cpp_int.hpp>

using namespace std;
// using namespace atcoder;
// using bint = boost::multiprecision::cpp_int;
using ll = long long;
using ull = unsigned long long;
using P = pair<int,int>;
using vi = vector<ll>;
using vvi = vector<vi>;
using vvvi = vector<vvi>;
#define rep(i,n) for(ll i = 0;i < (ll)n;i++)
#define ALL(x) (x).begin(),(x).end()
#define sz(c) ((ll)(c).size())
#define LB(A,x) (int)(lower_bound(A.begin(),A.end(),x)-A.begin())
// #define MOD 1000000007
#define MOD 998244353
template<typename T>using min_priority_queue=priority_queue<T,vector<T>,greater<T>>;
template<typename T>ostream&operator<<(ostream&os,vector<T>&v){for(int i = 0;i < v.size();i++)os<<v[i]<<(i+1!=v.size()?" ":"");return os;}
template<typename T>istream&operator>>(istream&is,vector<T>&v){for(T&in:v)is>>in;return is;}
template<typename T1,typename T2>ostream&operator<<(ostream&os,pair<T1,T2>&p){os<<p.first<<" "<<p.second;return os;}
template<typename T1,typename T2>istream&operator>>(istream&is,pair<T1,T2>&p){is>>p.first>>p.second;return is;}

// https://beet-aizu.github.io/library/tree/heavylightdecomposition.cpp
struct HLD{
  /*
  v := 隣接リスト
  id := 元々の頂点のHLD後の頂点番号。
  head := heavyな辺でつながれた頂点集合の代表元。もっとも浅い頂点。
  sub_sz := 部分木のサイズ
  par := 親
  inv := HLD後の頂点番号から元々の頂点番号をかえす

  add_edge(a,b) := a,b間の無向辺を追加
  build() := HLD 分解を実行。内部でdfs_szによる部分木の取得、dfs_hldによるHLD実行。
  lca(a,b) := a,bのLCAを返す
  for_each(a,b,f) := 頂点a,b間の頂点(a,bを含む)にfを実行。このfにseg木などが乗る。
  for_each_edge(a,b,f) := 頂点a,b間の辺にfを実行。辺はつながっている２つの頂点の内深い方と対応させる。
  */
  vector<vector<int>> v;
  vector<int> id,head,sub_sz,par,inv;
  HLD(int n) : v(n),id(n,-1),head(n),sub_sz(n,1),par(n,-1),inv(n){}

  void add_edge(int a,int b){
    v[a].push_back(b);
    v[b].push_back(a);
  }

  void dfs_sz(int ov,int pre){
    if(v[ov].size() && v[ov][0] == pre)swap(v[ov][0],v[ov].back());
    par[ov] = pre;
    for(auto &nv : v[ov]){
      if(nv == pre)continue;
      dfs_sz(nv,ov);
      sub_sz[ov] += sub_sz[nv];
      if(sub_sz[nv] > sub_sz[v[ov][0]])swap(nv,v[ov][0]);
    }
  }
  void dfs_hld(int ov,int &pos){
    id[ov] = pos++;
    inv[id[ov]] = ov;
    for(auto nv : v[ov]){
      if(nv == par[ov])continue;
      head[nv] = (nv == v[ov][0] ? head[ov] : nv);
      dfs_hld(nv,pos);
    }
  }
  void build(int root = 0){
    int pos = 0;
    dfs_sz(root,-1);
    head[root] = root;
    dfs_hld(root,pos);
  }
  int lca(int a,int b){
    while(1){
      if(id[a] > id[b])swap(a,b);
      if(head[a] == head[b])return a;
      b = par[head[b]];
    }
  }
  template<typename F>
  void for_each(int a,int b,const F& f){
    while(1){
      if(id[a] > id[b])swap(a,b);
      f(max(id[head[b]],id[a]),id[b]+1);
      if(head[a] != head[b])b = par[head[b]];
      else break;
    }
  }
  /*
  関数内でfは半開区間で値を取得するように正規化するので，
  BITが閉区間で実装されている場合などは
  auto f = [&](int a,int b){return bit.sum(a,b-1);};
  の様に変更する．データ構造が半開区間で値を取得する場合は
  auto f = [&](int a,int b){return seg.get(a,b);};
  でよい．
  */
  template<typename F>
  void for_each_edge(int a,int b,const F& f){
    while(1){
      if(id[a] > id[b])swap(a,b);
      if(head[a] != head[b]){
        f(id[head[b]],id[b]+1);
        b = par[head[b]];
      }else{
        if(a != b)f(id[a]+1,id[b]+1);
        break;
      }
    }
  }
};
template<typename T>
class Segtree{
  public:
  using F = function<T(T,T)>;
  int n;
  F f;
  T ti;
  vector<T> dat;
  Segtree(){};
  Segtree(F f,T ti): f(f),ti(ti){}
  void init(int N){
    n = 1;
    while(n < N)n <<= 1;
    dat.assign(n << 1,ti);
  }
  void build(const vector<T> &v){
    int N = v.size();
    init(N);
    for(int i = 0;i < N;i++)dat[n+i] = v[i];
    for(int i = n-1;i >= 0;i--)dat[i] = f(dat[(i << 1)|0],dat[(i << 1)|1]);
  }
  void set(int k,T x){
    dat[k+=n] = x;
    while(k >>= 1){
      dat[k] = f(dat[(k << 1)|0],dat[(k << 1)|1]);
    }
  }
  T get(int l,int r){
    T vl = ti,vr = ti;
    for(l += n,r += n;l < r;l >>= 1,r >>= 1){
      if(l & 1)vl = f(vl,dat[l++]);
      if(r & 1)vr = f(dat[--r],vr);
    }
    return f(vl,vr);
  }
  // max t s.t. check(query(st,t))
  template<typename C>
  int bs(int st,C &check,T &acc,int k,int l,int r){
    if(r-l <= 1){
      acc = f(acc,dat[k]);
      return check(acc) ? k-n : -1;
    }
    int mid = (l+r) >> 1;
    if(mid <= st)return bs(st,check,acc,(k << 1)|1,mid,r);
    if(st <= l && !check(f(acc,dat[k]))){
      acc = f(acc,dat[k]);
      return -1;
    }
    int vl = bs(st,check,acc,(k << 1)|0,l,mid);
    if(vl != -1)return vl;
    return bs(st,check,acc,(k << 1)|1,mid,r);
  }
  template<typename C>
  int bs(int st,C &check){
    T acc = ti;
    return bs(st,check,acc,1,0,n);
  }
};

struct e{
  ll a,b,x;
  e(ll a = -1,ll b = -1,ll x = 0):a(a),b(b),x(x){}
};
// https://beet-aizu.github.io/library/test/aoj/0367.test.cpp
// https://onlinejudge.u-aizu.ac.jp/challenges/search/titles/0367

int main(){
  
  ios_base::sync_with_stdio(0), cin.tie(0);
  ll n,K;
  cin >> n >> K;
  HLD hld(n);
  map<P,ll> mp;
  rep(i,n-1){
    ll a,b,c;cin >> a >> b >> c;
    hld.add_edge(a,b);
    mp[P{a,b}] = mp[P{b,a}] = c;
  }
  hld.build();
  vi w(n),dep(n,-1);
  auto dfs = [&](auto &dfs,ll ov,ll d)->void{
    dep[ov] = d;
    for(auto nv : hld.v[ov]){
      if(dep[nv] != -1)continue;
      dfs(dfs,nv,d + mp[{ov,nv}]);
    }
  };
  dfs(dfs,0,0);
  auto f = [&](e u,e v)->e{
    if(u.a < 0)return v;
    if(v.a < 0)return u;
    rep(i,2){
      rep(j,2){
        if(u.a == hld.par[v.a] || v.a == hld.par[u.a]){
          return e{u.b,v.b,u.x + v.x + ((w[u.a] + w[v.a] + abs(dep[u.a]-dep[v.a]))%K == 0 ? 0 : w[u.a] + w[v.a] + abs(dep[u.a]-dep[v.a]))};
        }
        swap(v.a,v.b);
      }
      swap(u.a,u.b);
    }
    return e{-1,-1,0};
  };
  Segtree<e> seg(f,e{-1,-1,0});
  {
    vector<e> vs(n,e{-1,-1,0});
    rep(i,n)vs[hld.id[i]].a = vs[hld.id[i]].b = i;
    seg.build(vs);
  }
  ll Q;cin >> Q;
  while(Q--){
    string s;cin >> s;
    if(s == "add"){
      ll x,d;cin >> x >> d;
      w[x] += d;
      seg.set(hld.id[x],e{x,x,0});
    }else{
      ll s,t;cin >> s >> t;
      e r1,r2;
      auto g = [&](ll a,ll b)->void{r1 = f(r1,seg.get(a,b));};
      auto gg = [&](ll a,ll b)->void{r2 = f(r2,seg.get(a,b));};
      ll u = hld.lca(s,t);
      hld.for_each(s,u,g);
      hld.for_each_edge(t,u,gg);
      cout << f(r1,r2).x << "\n";
    }
  }
  
  

  return 0;
}
accuracy: 1
time_ms: 160
memory_kb: 44028
final_rank: 10

code_snippet: #include<bits/stdc++.h>
// #include<atcoder/all>
// #include<boost/multiprecision/cpp_int.hpp>

using namespace std;
// using namespace atcoder;
// using bint = boost::multiprecision::cpp_int;
using ll = long long;
using ull = unsigned long long;
using P = pair<int,int>;
using vi = vector<ll>;
using vvi = vector<vi>;
using vvvi = vector<vvi>;
#define rep(i,n) for(ll i = 0;i < (ll)n;i++)
#define ALL(x) (x).begin(),(x).end()
#define sz(c) ((ll)(c).size())
#define LB(A,x) (int)(lower_bound(A.begin(),A.end(),x)-A.begin())
// #define MOD 1000000007
#define MOD 998244353
template<typename T>using min_priority_queue=priority_queue<T,vector<T>,greater<T>>;
template<typename T>ostream&operator<<(ostream&os,vector<T>&v){for(int i = 0;i < v.size();i++)os<<v[i]<<(i+1!=v.size()?" ":"");return os;}
template<typename T>istream&operator>>(istream&is,vector<T>&v){for(T&in:v)is>>in;return is;}
template<typename T1,typename T2>ostream&operator<<(ostream&os,pair<T1,T2>&p){os<<p.first<<" "<<p.second;return os;}
template<typename T1,typename T2>istream&operator>>(istream&is,pair<T1,T2>&p){is>>p.first>>p.second;return is;}

// https://beet-aizu.github.io/library/tree/heavylightdecomposition.cpp
struct HLD{
  /*
  v := 隣接リスト
  id := 元々の頂点のHLD後の頂点番号。
  head := heavyな辺でつながれた頂点集合の代表元。もっとも浅い頂点。
  sub_sz := 部分木のサイズ
  par := 親
  inv := HLD後の頂点番号から元々の頂点番号をかえす

  add_edge(a,b) := a,b間の無向辺を追加
  build() := HLD 分解を実行。内部でdfs_szによる部分木の取得、dfs_hldによるHLD実行。
  lca(a,b) := a,bのLCAを返す
  for_each(a,b,f) := 頂点a,b間の頂点(a,bを含む)にfを実行。このfにseg木などが乗る。
  for_each_edge(a,b,f) := 頂点a,b間の辺にfを実行。辺はつながっている２つの頂点の内深い方と対応させる。
  */
  vector<vector<int>> v;
  vector<int> id,head,sub_sz,par,inv;
  HLD(int n) : v(n),id(n,-1),head(n),sub_sz(n,1),par(n,-1),inv(n){}

  void add_edge(int a,int b){
    v[a].push_back(b);
    v[b].push_back(a);
  }

  void dfs_sz(int ov,int pre){
    if(v[ov].size() && v[ov][0] == pre)swap(v[ov][0],v[ov].back());
    par[ov] = pre;
    for(auto &nv : v[ov]){
      if(nv == pre)continue;
      dfs_sz(nv,ov);
      sub_sz[ov] += sub_sz[nv];
      if(sub_sz[nv] > sub_sz[v[ov][0]])swap(nv,v[ov][0]);
    }
  }
  void dfs_hld(int ov,int &pos){
    id[ov] = pos++;
    inv[id[ov]] = ov;
    for(auto nv : v[ov]){
      if(nv == par[ov])continue;
      head[nv] = (nv == v[ov][0] ? head[ov] : nv);
      dfs_hld(nv,pos);
    }
  }
  void build(int root = 0){
    int pos = 0;
    dfs_sz(root,-1);
    head[root] = root;
    dfs_hld(root,pos);
  }
  int lca(int a,int b){
    while(1){
      if(id[a] > id[b])swap(a,b);
      if(head[a] == head[b])return a;
      b = par[head[b]];
    }
  }
  template<typename F>
  void for_each(int a,int b,const F& f){
    while(1){
      if(id[a] > id[b])swap(a,b);
      f(max(id[head[b]],id[a]),id[b]+1);
      if(head[a] != head[b])b = par[head[b]];
      else break;
    }
  }
  /*
  関数内でfは半開区間で値を取得するように正規化するので，
  BITが閉区間で実装されている場合などは
  auto f = [&](int a,int b){return bit.sum(a,b-1);};
  の様に変更する．データ構造が半開区間で値を取得する場合は
  auto f = [&](int a,int b){return seg.get(a,b);};
  でよい．
  */
  template<typename F>
  void for_each_edge(int a,int b,const F& f){
    while(1){
      if(id[a] > id[b])swap(a,b);
      if(head[a] != head[b]){
        f(id[head[b]],id[b]+1);
        b = par[head[b]];
      }else{
        if(a != b)f(id[a]+1,id[b]+1);
        break;
      }
    }
  }
};
template<typename T>
class Segtree{
  public:
  using F = function<T(T,T)>;
  int n;
  F f;
  T ti;
  vector<T> dat;
  Segtree(){};
  Segtree(F f,T ti): f(f),ti(ti){}
  void init(int N){
    n = 1;
    while(n < N)n <<= 1;
    dat.assign(n << 1,ti);
  }
  void build(const vector<T> &v){
    int N = v.size();
    init(N);
    for(int i = 0;i < N;i++)dat[n+i] = v[i];
    for(int i = n-1;i >= 0;i--)dat[i] = f(dat[(i << 1)|0],dat[(i << 1)|1]);
  }
  void set(int k,T x){
    dat[k+=n] = x;
    while(k >>= 1){
      dat[k] = f(dat[(k << 1)|0],dat[(k << 1)|1]);
    }
  }
  T get(int l,int r){
    T vl = ti,vr = ti;
    for(l += n,r += n;l < r;l >>= 1,r >>= 1){
      if(l & 1)vl = f(vl,dat[l++]);
      if(r & 1)vr = f(dat[--r],vr);
    }
    return f(vl,vr);
  }
  // max t s.t. check(query(st,t))
  template<typename C>
  int bs(int st,C &check,T &acc,int k,int l,int r){
    if(r-l <= 1){
      acc = f(acc,dat[k]);
      return check(acc) ? k-n : -1;
    }
    int mid = (l+r) >> 1;
    if(mid <= st)return bs(st,check,acc,(k << 1)|1,mid,r);
    if(st <= l && !check(f(acc,dat[k]))){
      acc = f(acc,dat[k]);
      return -1;
    }
    int vl = bs(st,check,acc,(k << 1)|0,l,mid);
    if(vl != -1)return vl;
    return bs(st,check,acc,(k << 1)|1,mid,r);
  }
  template<typename C>
  int bs(int st,C &check){
    T acc = ti;
    return bs(st,check,acc,1,0,n);
  }
};

struct e{
  ll a,b,x;
  e(ll a = -1,ll b = -1,ll x = 0):a(a),b(b),x(x){}
};
// https://beet-aizu.github.io/library/test/aoj/0367.test.cpp
// https://onlinejudge.u-aizu.ac.jp/challenges/search/titles/0367

int main(){
  
  ios_base::sync_with_stdio(0), cin.tie(0);
  int n,K;
  cin >> n >> K;
  HLD hld(n);
  map<P,int> mp;
  rep(i,n-1){
    int a,b,c;cin >> a >> b >> c;
    hld.add_edge(a,b);
    mp[P{a,b}] = mp[P{b,a}] = c;
  }
  hld.build();
  vi w(n),dep(n,-1);
  auto dfs = [&](auto &dfs,int ov,int d)->void{
    dep[ov] = d;
    for(auto nv : hld.v[ov]){
      if(dep[nv] != -1)continue;
      dfs(dfs,nv,d + mp[{ov,nv}]);
    }
  };
  dfs(dfs,0,0);
  auto f = [&](e u,e v)->e{
    if(u.a < 0)return v;
    if(v.a < 0)return u;
    rep(i,2){
      rep(j,2){
        if(u.a == hld.par[v.a] || v.a == hld.par[u.a]){
          return e{u.b,v.b,u.x + v.x + ((w[u.a] + w[v.a] + abs(dep[u.a]-dep[v.a]))%K == 0 ? 0 : w[u.a] + w[v.a] + abs(dep[u.a]-dep[v.a]))};
        }
        swap(v.a,v.b);
      }
      swap(u.a,u.b);
    }
    return e{-1,-1,0};
  };
  Segtree<e> seg(f,e{-1,-1,0});
  {
    vector<e> vs(n,e{-1,-1,0});
    rep(i,n)vs[hld.id[i]].a = vs[hld.id[i]].b = i;
    seg.build(vs);
  }
  int Q;cin >> Q;
  while(Q--){
    string s;cin >> s;
    if(s == "add"){
      int x,d;cin >> x >> d;
      w[x] += d;
      seg.set(hld.id[x],e{x,x,0});
    }else{
      int s,t;cin >> s >> t;
      e r1,r2;
      auto g = [&](int a,int b)->void{r1 = f(r1,seg.get(a,b));};
      auto gg = [&](int a,int b)->void{r2 = f(r2,seg.get(a,b));};
      int u = hld.lca(s,t);
      hld.for_each(s,u,g);
      hld.for_each_edge(t,u,gg);
      cout << f(r1,r2).x << "\n";
    }
  }
  
  

  return 0;
}
accuracy: 0.9523809523809523
time_ms: 160
memory_kb: 43956
final_rank: 20

code_snippet: #include <bits/stdc++.h>

struct Edge {
    Edge(int f, int t, long long w, int i): from(f), to(t), weight(w), id(i) {}
    int from;
    int to;
    long long weight;
    int id;
};

const int ROOT = 0;

struct Doubling {
    static constexpr int LOG = 20;
    std::vector<std::vector<int>> parent;
    std::vector<int> depth;
    Doubling(const std::vector<std::vector<Edge>>& graph) {
        int n = graph.size();
        parent.resize(LOG + 1, std::vector<int>(n));
        depth.resize(n);
        auto dfs = [&](auto self, int u, int p) -> void {
            parent[0][u] = p;
            for (auto e: graph[u]) {
                if (e.to == p) continue;
                depth[e.to] = depth[u] + 1;
                self(self, e.to, u);
            }
        };
        dfs(dfs, ROOT, ROOT);
        for (int i = 0; i < LOG; i++) {
            for (int j = 0; j < n; j++) {
                parent[i + 1][j] = parent[i][parent[i][j]];
            }
        }
    }
    int lca(int lhs, int rhs) {
        if (depth[lhs] > depth[rhs]) std::swap(lhs, rhs);
        int diff = depth[rhs] - depth[lhs];
        for (int i = 0; i < LOG; i++) {
            if (diff & (1 << i)) {
                rhs = parent[i][rhs];
            }
        }
        assert(depth[lhs] == depth[rhs]);
        if (lhs == rhs) return lhs;
        for (int i = LOG; i >= 0; i--) {
            if (parent[i][lhs] != parent[i][rhs]) {
                lhs = parent[i][lhs];
                rhs = parent[i][rhs];
            }
        }
        assert(lhs != rhs);
        assert(parent[0][lhs] == parent[0][rhs]);
        return parent[0][lhs];
    }
    // u -> w -> .... -> v
    // となるwを探す
    int child(int u, int v) {
        assert(depth[u] < depth[v]);
        int diff = depth[v] - depth[u] - 1;
        for (int i = 0; i < LOG; i++) {
            if (diff & (1 << i)) {
                v = parent[i][v];
            }
        }
        assert(depth[u] + 1 == depth[v]);
        assert(parent[0][v] == u);
        return v;
    }
};

struct FenwickTreeWithEulerTour {
    std::vector<int> in, out;
    std::vector<long long> data;
    int n;
    FenwickTreeWithEulerTour(const std::vector<std::vector<Edge>>& graph) {
        n = 2 * graph.size();
        in.resize(graph.size());
        out.resize(graph.size());
        data.resize(n + 1);
        int now = 0;
        auto dfs = [&](auto self, int u, int p) -> void {
            in[u] = now++;
            for (auto e: graph[u]) {
                if (e.to == p) continue;
                self(self, e.to, u);
            }
            out[u] = now++;
        };
        dfs(dfs, ROOT, ROOT);
    }

    void clear() {
        data.assign(n + 1, 0);
    }

    // uはvの祖先かどうか
    bool is_ancestor(int u, int v) {
        return in[u] <= in[v] && out[v] <= out[u];
    }

    long long dist(int u, int v, int w) {
        return path(u) + path(v) - 2 * path(w);
    }

    long long path(int u) {
        return sum(in[u]);
    }

    void add_edge(int u, long long diff) {
        add(in[u] - 1, diff);
        add(out[u] - 1, -diff);
    }

    void add(int pos, long long diff) {
        for (pos++; pos <= n; pos += pos & -pos) {
            data[pos] += diff;
        }
    }

    long long sum(int pos) {
        long long res = 0;
        for (; pos > 0; pos -= pos & -pos) {
            res += data[pos];
        }
        return res;
    }
};

int main() {
    int n;
    long long k;
    std::cin >> n >> k;
    std::vector<Edge> edges;
    edges.reserve(n - 1);
    std::vector graph(n, std::vector<Edge>());
    for (int i = 0; i + 1 < n; i++) {
        int u, v;
        long long w;
        std::cin >> u >> v >> w;
        edges.emplace_back(u, v, w, i);
        graph[u].emplace_back(u, v, w, i);
        graph[v].emplace_back(v, u, w, i);
    }
    Doubling doubling(graph);
    FenwickTreeWithEulerTour fenwick(graph);
    for (auto& e: edges) {
        if (fenwick.is_ancestor(e.to, e.from)) std::swap(e.from, e.to);
    }

    std::vector<int> parent_edge_id(n);
    auto dfs = [&](auto self, int u, int p) -> void {
        for (auto e: graph[u]) {
            if (e.to == p) {
                parent_edge_id[u] = e.id;
            } else {
                self(self, e.to, u);
            }
        }
    };
    dfs(dfs, ROOT, ROOT);

    auto eval = [&](long long w) {
        return w % k == 0 ? 0 : w;
    };

    int q;
    std::cin >> q;
    const int SQRT_Q = 500;
    std::vector<long long> cur_diff(n);
    std::vector<int> already(n);
    for (; q > 0; q -= SQRT_Q) {
        cur_diff.assign(n, 0);
        already.assign(n, 0);
        fenwick.clear();
        for (auto e: edges) {
            fenwick.add_edge(e.to, eval(e.weight));
        }

        std::vector<int> add_query_vertex;
        for (int i = 0; i < std::min(q, SQRT_Q); i++) {
            std::string type;
            std::cin >> type;
            if (type == "add") {
                int x, d;
                std::cin >> x >> d;
                cur_diff[x] += d;
                if (!already[x]) {
                    already[x] = 1;
                    add_query_vertex.push_back(x);
                }
            } else {
                int u, v;
                std::cin >> u >> v;
                int w = doubling.lca(u, v);
                // u -> v
                if (v == w) std::swap(u, v);
                long long ans = fenwick.dist(u, v, w);
                std::unordered_map<int, long long> edge_weight_diff;
                for (auto a: add_query_vertex) {
                    bool on_path = true;
                    if (u == w) {
                        on_path &= fenwick.is_ancestor(u, a);
                        on_path &= fenwick.is_ancestor(a, v);
                    } else {
                        on_path &= fenwick.is_ancestor(w, a);
                        bool temp = false;
                        temp |= fenwick.is_ancestor(a, u);
                        temp |= fenwick.is_ancestor(a, v);
                        on_path &= temp;
                    }
                    if (on_path) {
                        for (auto b: {u, v}) {
                            if (b == a) continue;
                            if (fenwick.is_ancestor(a, b)) {
                                int c = doubling.child(a, b);
                                assert(edges[parent_edge_id[c]].from == a);
                                edge_weight_diff[parent_edge_id[c]] += cur_diff[a];
                                // ans -= eval(edges[parent_edge_id[c]].weight);
                                // ans += eval(edges[parent_edge_id[c]].weight + cur_diff[a]);
                            } else {
                                edge_weight_diff[parent_edge_id[a]] += cur_diff[a];
                                // ans -= eval(edges[parent_edge_id[a]].weight);
                                // ans += eval(edges[parent_edge_id[a]].weight + cur_diff[a]);
                            }
                        }
                    }
                }
                for (auto [id, diff]: edge_weight_diff) {
                    ans -= eval(edges[id].weight);
                    ans += eval(edges[id].weight + diff);
                }
                std::cout << ans << '\n';
            }
        }

        for (auto u: add_query_vertex) {
            for (auto e: graph[u]) {
                edges[e.id].weight += cur_diff[u];
            }
        }
    }
}
accuracy: 1
time_ms: 960
memory_kb: 35764
final_rank: 14

code_snippet: #include<bits/stdc++.h>
#define PPque priority_queue<tuple<ll, ll, ll>, vector<tuple<ll, ll, ll>>, greater<tuple<ll, ll, ll>>>
#define Pque  priority_queue<pair<ll, ll>, vector<pair<ll, ll>>, greater<pair<ll, ll>>>
#define pque priority_queue<ll, vector<ll>, greater<ll>>
#define umap unordered_map
#define uset unordered_set
#define rep(i, s, f) for(ll i = s; i <= f; i++)
#define per(i, s, f) for(ll i = s; i >= f; i--)
#define all0(x) (x).begin() ,(x).end()
#define all(x) (x).begin() + 1, (x).end()
#define vvvvl vector<vvvl>
#define vvvi vector<vector<vector<int>>>
#define vvvl vector<vector<vector<ll>>>
#define vvvc vector<vector<vector<char>>>
#define vvvd vector<vector<vector<double>>>
#define vvvm vector<vector<vector<mint>>>
#define vvi vector<vector<int>>
#define vvl vector<vector<ll>>
#define vvs vector<vector<string>>
#define vvc vector<vector<char>>
#define vvp vector<vector<pair<ll, ll>>>
#define vvb vector<vector<bool>>
#define vvd vector<vector<double>>
#define vvm vector<vector<mint>>
#define vp vector<pair<ll, ll>>
#define vi vector<int>
#define vl vector<ll>
#define vs vector<string>
#define vc vector<char>
#define vb vector<bool>
#define vd vector<double>
#define vm vector<mint>
#define P pair<ll, ll>
#define TU tuple<ll, ll, ll>
#define rrr(l, r) mt()%(r-l+1)+l
#define ENDL '\n'
#define ull unsigned long long
typedef long long ll;
using namespace std;

////////////////////////////////////////////////////////////////////////////////////////////////////////////
//これが本当の組み込み関数ってね（笑）

template <typename T>
T or_less(vector<T> &A, T x) { //x以下で最大要素の添字 前提: sort済み 存在しない: -1
  return distance(A.begin(), upper_bound(A.begin(), A.end(), x)-1);
}
template <typename T>
T under(vector<T> &A, T x) { //x未満の最大要素の添字　前提: sort済み 存在しない: -1
  return distance(A.begin(), lower_bound(A.begin(), A.end(), x)-1);
}
template <typename T>
T or_more(vector<T> &A, T x) { //x以上で最小要素の添字　　前提: sort済み 存在しない: N .   //distanceのA.beginは添字を出すために常にA.begin() NG: A.begin() + 1
  return distance(A.begin(), lower_bound(A.begin(), A.end(), x));
}
template <typename T>
T over(vector<T> &A, T x) { //xより大きい最小要素の添字前提: sort済み 存在しない: N
  return distance(A.begin(), upper_bound(A.begin(), A.end(), x));
}
template <typename T>
vector<T> vec_shift_right(vector<T> A, ll step, ll dir = 1, ll indexed = 1) {//dir = 1 : 右シフト  dir = -1 : 左シフト
  ll N = A.size() - indexed;
  vector<T> res(N+1);
  rep(i, indexed, N) {
    ll idx = i - step * dir;
    if(idx < indexed) idx += N;
    if(idx > N) idx -= N;
    res.at(i) = A.at(idx);
  }
  return res;
}
template <typename T>
void UNIQUE(vector<T> &A) {
    sort(all0(A));
  return A.erase(unique(A.begin(), A.end()), A.end());
}
template <typename T>
void rev90(vector<vector<T>> &A, int indexed = 1) {
  reverse(A.begin() + indexed, A.end());
  int n = A.size();
  rep(i, indexed, n-1) {
   rep(j, i+1, n-1) {
      swap(A.at(i).at(j), A.at(j).at(i));
    }
  }
}
void chmin(ll &a, ll b) {
  a = min(a, b);
}
void chmax(ll &a, ll b) {
  a = max(a, b);
}
//////////////////////////////////////////////////////////////////////
//数学系
///////////////////////////////////////////////////////////////////////
ll round(ll x, ll i) {return ll(x + 5 * pow(10, i-1))/ll(pow(10, i)) * ll(pow(10, i));}
vp insu_bunkai(ll N) {
  vp res;
  for (ll i = 2; i * i <= N; i++) {
    ll cnt = 0;
    while(N % i == 0) {
      cnt++;
      N /= i;
    }
    if(cnt != 0) res.push_back(P(i, cnt));
  }
  if(N != 1) res.push_back(P(N, 1));
  return res;
}
ll extgcd (ll a, ll b,  ll &x, ll &y) {
  if(b == 0) { x = 1;y = 0;return a;}
  ll d = extgcd(b, a%b, y, x);
  y -= a/b * x;
  return d;
}
template <typename T>
T ceil(T a, T b) {
  assert(b != 0);
  if(a % b == 0) return a / b;
  if((a <= 0 && b < 0) || (a >= 0 && b > 0)) return a/b + 1;
  else return a / b;
}
template <typename T>
T floor(T a, T b) {
  assert(b != 0);
  if(a % b == 0) return a / b;
  if((a <= 0 && b < 0) || (a >= 0 && b > 0)) return a/b;
  else return a/b - 1;
}
ll modpow(ll x, ll y, ll mod) {
  if(x > mod) x %= mod;
  if(y == 0) return 1;
  ll res = modpow(x, y >> 1, mod);
  res = res * res % mod;
  if(y & 1) res *= x, res %= mod;
  return res;
}
ll sqrt_(ll a) {
    ll l = 0;
    ll r = 3000000000LL;
    while(l < r) {
        ll mid = (l + r + 1) / 2;
        if(mid * mid <= a) l = mid;
        else r = mid - 1;
    }
    return l;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//グローバル変数を置くところ（情報工学意識高め）
//#pragma GCC optimize("O3")
//#pragma GCC optimize("unroll-loops")
const ll int_max = 1001001001;
const ll ll_max = 1001001001001001001LL;
const double pi = 3.141592653589793;
vl dx{0, 1, 0, -1, 0, 1, 1, -1, -1};  // (番兵) → ↓ ← ↑  ※ 右から時計回り　
vl dy{0, 0, -1, 0, 1, 1, -1, -1, 1};
//const ll mod = 1000000007;
//const ll mod = 998244353;
ll K;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////////////////////////////////////
//いじるところ。




//////////////////////////////////////////////////////////////////////////////////////////////////////////

template <typename X>
struct HL {
  vector<int> siz;//[元の頂点番号] = サイズ
  vector<int> num;//[元の頂点番号] = 振り直した頂点番号
  vector<int> numrev;
  vector<int> par;//[振られた] = 振られた 自分の親
  vector<int> head;//[振られた番号] = 振られた番号 自分の連結成分の頭
  int N;
  vector<X> dat;//セグ木用の配列、0...上り 1...下り
  
  int n = 1;
  X def;
  X ide;
  

  HL(int _N, vvl& G, X _def, X _ide): N(_N), def(_def), ide(_ide) {
    par.resize(N+1);
    iota(par.begin(), par.end(), 0);
    siz.resize(N+1, 1);
    num.assign(N+1, -1);
    numrev.resize(N+1, -1);
    head.resize(N+1);
    while(n < N) n <<= 1;
    dat.assign(n*2, def);

    auto dfs_siz = [&](auto dfs_siz, int now, int prev) -> void {
      int sum = 1;
      for(int to : G[now]) {
        if(to == prev) continue;
         dfs_siz(dfs_siz, to, now);
         sum += siz[to];
      }
      siz[now] = sum;
      return;
    };
    dfs_siz(dfs_siz, 1, -1);
    rep(i, 1, N){
      sort(G[i].begin(), G[i].end(), [&](int a, int b) {
        return siz[a] > siz[b];
      });
    }

    int idx = 1;
    auto dfs_bunkai = [&](auto dfs_bunkai, int now, int prev, int hed) -> void {
      num[now] = idx;//番号付
      numrev[idx] = now;
      idx++;
      par[num[now]] = prev;//親の頂点   //1だけは直前も自分も1
      if(hed == -1)hed = num[now];
      head[num[now]] = hed;

      bool flag = true;
      
      rep(i, 0, int(G[now].size()) - 1) {
        if(num[G[now][i]] != -1) continue;
        if(flag) dfs_bunkai(dfs_bunkai, G[now][i], num[now], hed), flag = false;
        else dfs_bunkai(dfs_bunkai, G[now][i], num[now], -1);
      }
      return;
    };
    dfs_bunkai(dfs_bunkai, 1, 1, -1);
  }
  // 振り終わった
  //////////////////////////////////////////
  
  
  void change(int pos, ll x, int t) {
    pos = num[pos];//振り当てた番号で扱う！！！
    pos = n + pos - 1;
    dat[pos] = update(dat[pos], x, t);
    while(pos >= 2) {
      pos >>= 1;
      dat[pos] = op(dat[pos * 2], dat[pos * 2 + 1]);
    }
  }

  int getlca(int a, int b) {
    a = num[a];
    b = num[b];
    while(true) {
      if(a > b) swap(a, b);
      if(head[a] == head[b]) return numrev[a];
      b = par[head[b]];
    }
  }

  X query(int a, int b) {
    X L = ide, R = ide;
    a += n - 1, b += n - 1;
    while(a < b) {
      if(a & 1) L = op(L, dat[a++]);
      if(b & 1) R = op(dat[--b], R);
      a >>= 1, b >>= 1;
    }  
    return  op(L, R);
  }

  ll get(int u, int v) {//uからvへのパス上の頂点についてやる
    int w = num[getlca(u, v)];//lcaで左右に分ける
    u = num[u];
    v = num[v];
    X L = ide;
    while(u != w) {
      int hed = max<int>(head[u], w+1);
      L = op(query(hed, u+1), L); 
      if(hed != w)  u = par[hed];
      else break;
    }   
    L = op(query(w, w+1), L);
    X R = ide;
    while(v != w) {
      int hed = max<int>(head[v], w+1);
      R = op(query(hed, v+1), R); 
      if(hed != w)  v = par[hed];
      else break;
    }
    R = op(query(w, w+1), R);
    //R = operation(query(1, size+1, w, w+1, 1), R); これをしてないので、RはまだLCAに辿り着いてない。
    return L.s + R.s;//交換則を要する時はこの行を変更する必要があるかもしれない : 無いと思うが
  }


  int parent(int a) {
    return numrev[par[num[a]]];
  }
};

struct Monoid {
  bool e = false; 
  ll l = 0, r = 0, s = 0;
  Monoid(){e = true;}
  Monoid(ll _l, ll _r, ll _s) : l(_l), r(_r), s(_s){e = false;}

  friend Monoid op(const Monoid a, const Monoid b) {
    if(a.e) return b;
    if(b.e) return a;
    Monoid res(0, 0, 0);
    res.l = a.l;
    res.r = b.r;
    res.s = a.s + b.s + a.r + b.l;
    if((a.r + b.l)%K == 0) res.s -= a.r + b.l;
    return res;
  }

  friend Monoid update(Monoid a, ll b, int t) {
    Monoid res = a;
    res.l += b;
    if(t == 1)res.r += b;
    return res;
  }
};
Monoid ide;//単位元
Monoid def(0, 0, 0);//初期化の値

void solve() {
  ll N;
  cin >> N >> K;
  vvl G(N+1);
  map<P, ll> w;
  rep(i,1,N-1) {
    ll a, b;
    cin >> a >> b; a++, b++;
    G[a].push_back(b);
    G[b].push_back(a);
    ll c;
    cin >> c;
    w[P(a, b)] = c;
  }

  
  HL<Monoid> hld(N, G, def, ide);

  for(auto [p, c] : w) {
    auto [a, b] = p;
    if(hld.parent(a) == b) swap(a, b);
    hld.change(b, c, 0);
  }
  ll Q;
  cin >> Q;
  rep(qi, 1, Q) {
    string type;
    cin >> type;
    if(type == "add") {
      ll x, d;
      cin >> x >> d;
      x++;
      hld.change(x, d, 1);
    }
    else {
      ll s, t;
      cin >> s >> t; s++, t++;
      cout << hld.get(s, t) << endl;
    }
  }
 

    

}
//無断で0を平方数にカウントする人もいる
//”部分文字列”と”連続部分文字列”は違うので確認すること
//一般のグラフと、有向辺かつその貼り方に制約がある(多くの場合：番号がで解放に伸びる）はだいぶ違うので確認すること
//座標を2で割った時の”切り捨て側(左側）”を求めるには 誤:(x / 2) マイナスの時！！！ 正:floor<ll>(x, 2);
//stringでの数字の下から1桁目は 正:S.at(N-1)  誤:S.at(0)
//if(S.at(i) == 1)　← charなのに1...?
// modは取りましたか...?(´・ω・｀)
//sortの比較関数は、 a == b　ならば falseを返す必要がある（そうで無いとRE(発生しない場合もある））



int main() {
  ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  cout << fixed << setprecision(15);
    ll T = 1;
    //cin >> T;
    rep(i, 1, T) {
        solve();
    }
  return 0;
}
accuracy: 1
time_ms: 180
memory_kb: 34020
final_rank: 4

code_snippet: #include<bits/stdc++.h>
#define PPque priority_queue<tuple<ll, ll, ll>, vector<tuple<ll, ll, ll>>, greater<tuple<ll, ll, ll>>>
#define Pque  priority_queue<pair<ll, ll>, vector<pair<ll, ll>>, greater<pair<ll, ll>>>
#define pque priority_queue<ll, vector<ll>, greater<ll>>
#define umap unordered_map
#define uset unordered_set
#define rep(i, s, f) for(ll i = s; i <= f; i++)
#define per(i, s, f) for(ll i = s; i >= f; i--)
#define all0(x) (x).begin() ,(x).end()
#define all(x) (x).begin() + 1, (x).end()
#define vvvvl vector<vvvl>
#define vvvi vector<vector<vector<int>>>
#define vvvl vector<vector<vector<ll>>>
#define vvvc vector<vector<vector<char>>>
#define vvvd vector<vector<vector<double>>>
#define vvvm vector<vector<vector<mint>>>
#define vvi vector<vector<int>>
#define vvl vector<vector<ll>>
#define vvs vector<vector<string>>
#define vvc vector<vector<char>>
#define vvp vector<vector<pair<ll, ll>>>
#define vvb vector<vector<bool>>
#define vvd vector<vector<double>>
#define vvm vector<vector<mint>>
#define vp vector<pair<ll, ll>>
#define vi vector<int>
#define vl vector<ll>
#define vs vector<string>
#define vc vector<char>
#define vb vector<bool>
#define vd vector<double>
#define vm vector<mint>
#define P pair<ll, ll>
#define TU tuple<ll, ll, ll>
#define rrr(l, r) mt()%(r-l+1)+l
#define ENDL '\n'
#define ull unsigned long long
typedef long long ll;
using namespace std;

////////////////////////////////////////////////////////////////////////////////////////////////////////////
//これが本当の組み込み関数ってね（笑）

template <typename T>
T or_less(vector<T> &A, T x) { //x以下で最大要素の添字 前提: sort済み 存在しない: -1
  return distance(A.begin(), upper_bound(A.begin(), A.end(), x)-1);
}
template <typename T>
T under(vector<T> &A, T x) { //x未満の最大要素の添字　前提: sort済み 存在しない: -1
  return distance(A.begin(), lower_bound(A.begin(), A.end(), x)-1);
}
template <typename T>
T or_more(vector<T> &A, T x) { //x以上で最小要素の添字　　前提: sort済み 存在しない: N .   //distanceのA.beginは添字を出すために常にA.begin() NG: A.begin() + 1
  return distance(A.begin(), lower_bound(A.begin(), A.end(), x));
}
template <typename T>
T over(vector<T> &A, T x) { //xより大きい最小要素の添字前提: sort済み 存在しない: N
  return distance(A.begin(), upper_bound(A.begin(), A.end(), x));
}
template <typename T>
vector<T> vec_shift_right(vector<T> A, ll step, ll dir = 1, ll indexed = 1) {//dir = 1 : 右シフト  dir = -1 : 左シフト
  ll N = A.size() - indexed;
  vector<T> res(N+1);
  rep(i, indexed, N) {
    ll idx = i - step * dir;
    if(idx < indexed) idx += N;
    if(idx > N) idx -= N;
    res.at(i) = A.at(idx);
  }
  return res;
}
template <typename T>
void UNIQUE(vector<T> &A) {
    sort(all0(A));
  return A.erase(unique(A.begin(), A.end()), A.end());
}
template <typename T>
void rev90(vector<vector<T>> &A, int indexed = 1) {
  reverse(A.begin() + indexed, A.end());
  int n = A.size();
  rep(i, indexed, n-1) {
   rep(j, i+1, n-1) {
      swap(A.at(i).at(j), A.at(j).at(i));
    }
  }
}
void chmin(ll &a, ll b) {
  a = min(a, b);
}
void chmax(ll &a, ll b) {
  a = max(a, b);
}
//////////////////////////////////////////////////////////////////////
//数学系
///////////////////////////////////////////////////////////////////////
ll round(ll x, ll i) {return ll(x + 5 * pow(10, i-1))/ll(pow(10, i)) * ll(pow(10, i));}
vp insu_bunkai(ll N) {
  vp res;
  for (ll i = 2; i * i <= N; i++) {
    ll cnt = 0;
    while(N % i == 0) {
      cnt++;
      N /= i;
    }
    if(cnt != 0) res.push_back(P(i, cnt));
  }
  if(N != 1) res.push_back(P(N, 1));
  return res;
}
ll extgcd (ll a, ll b,  ll &x, ll &y) {
  if(b == 0) { x = 1;y = 0;return a;}
  ll d = extgcd(b, a%b, y, x);
  y -= a/b * x;
  return d;
}
template <typename T>
T ceil(T a, T b) {
  assert(b != 0);
  if(a % b == 0) return a / b;
  if((a <= 0 && b < 0) || (a >= 0 && b > 0)) return a/b + 1;
  else return a / b;
}
template <typename T>
T floor(T a, T b) {
  assert(b != 0);
  if(a % b == 0) return a / b;
  if((a <= 0 && b < 0) || (a >= 0 && b > 0)) return a/b;
  else return a/b - 1;
}
ll modpow(ll x, ll y, ll mod) {
  if(x > mod) x %= mod;
  if(y == 0) return 1;
  ll res = modpow(x, y >> 1, mod);
  res = res * res % mod;
  if(y & 1) res *= x, res %= mod;
  return res;
}
ll sqrt_(ll a) {
    ll l = 0;
    ll r = 3000000000LL;
    while(l < r) {
        ll mid = (l + r + 1) / 2;
        if(mid * mid <= a) l = mid;
        else r = mid - 1;
    }
    return l;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//グローバル変数を置くところ（情報工学意識高め）
//#pragma GCC optimize("O3")
//#pragma GCC optimize("unroll-loops")
const ll int_max = 1001001001;
const ll ll_max = 1001001001001001001LL;
const double pi = 3.141592653589793;
vl dx{0, 1, 0, -1, 0, 1, 1, -1, -1};  // (番兵) → ↓ ← ↑  ※ 右から時計回り　
vl dy{0, 0, -1, 0, 1, 1, -1, -1, 1};
//const ll mod = 1000000007;
//const ll mod = 998244353;
ll K;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////////////////////////////////////
//いじるところ。
struct XX {
  bool e = false; 
  ll l = 0, r = 0, s = 0;
  XX(){e = true;}
  XX(ll _l, ll _r, ll _s) : l(_l), r(_r), s(_s){e = false;}
};
XX def;
XX gen(0, 0, 0);
XX operation(XX a, XX b) {
  if(a.e) return b;
  if(b.e) return a;
  XX res(0, 0, 0);
  res.l = a.l;
  res.r = b.r;
  res.s = a.s + b.s + a.r + b.l;
  if((a.r + b.l)%K == 0) res.s -= a.r + b.l;
  return res;
}

XX update(XX a, ll b, int t) {
  XX res = a;
  res.l += b;
  if(t == 1)res.r += b;
  return res;
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////

template <typename X>
struct HL {
  vector<int> siz;//[元の頂点番号] = サイズ
  vector<int> num;//[元の頂点番号] = 振り直した頂点番号
  vector<int> numrev;
  vector<int> par;//[振られた] = 振られた 自分の親
  vector<int> head;//[振られた番号] = 振られた番号 自分の連結成分の頭
  int N;
  vector<X> dat;//セグ木用の配列、0...上り 1...下り
  
  int n = 1;
  

  HL(int _N, vvl& G): N(_N) {
    par.resize(N+1);
    iota(par.begin(), par.end(), 0);
    siz.resize(N+1, 1);
    num.assign(N+1, -1);
    numrev.resize(N+1, -1);
    head.resize(N+1);
    while(n < N) n <<= 1;
    dat.assign(n*2, gen);

    auto dfs_siz = [&](auto dfs_siz, int now, int prev) -> void {
      int sum = 1;
      for(int to : G[now]) {
        if(to == prev) continue;
         dfs_siz(dfs_siz, to, now);
         sum += siz[to];
      }
      siz[now] = sum;
      return;
    };
    dfs_siz(dfs_siz, 1, -1);
    rep(i, 1, N){
      sort(G[i].begin(), G[i].end(), [&](int a, int b) {
        return siz[a] > siz[b];
      });
    }

    int idx = 1;
    auto dfs_bunkai = [&](auto dfs_bunkai, int now, int prev, int hed) -> void {
      num[now] = idx;//番号付
      numrev[idx] = now;
      idx++;
      par[num[now]] = prev;//親の頂点   //1だけは直前も自分も1
      if(hed == -1)hed = num[now];
      head[num[now]] = hed;

      bool flag = true;
      
      rep(i, 0, int(G[now].size()) - 1) {
        if(num[G[now][i]] != -1) continue;
        if(flag) dfs_bunkai(dfs_bunkai, G[now][i], num[now], hed), flag = false;
        else dfs_bunkai(dfs_bunkai, G[now][i], num[now], -1);
      }
      return;
    };
    dfs_bunkai(dfs_bunkai, 1, 1, -1);
  }
  // 振り終わった
  //////////////////////////////////////////
  
  
  void change(int pos, ll x, int t) {
    pos = num[pos];//振り当てた番号で扱う！！！
    pos = n + pos - 1;
    dat[pos] = update(dat[pos], x, t);
    while(pos >= 2) {
      pos >>= 1;
      dat[pos] = operation(dat[pos * 2], dat[pos * 2 + 1]);
    }
  }

  int getlca(int a, int b) {
    a = num[a];
    b = num[b];
    while(true) {
      if(a > b) swap(a, b);
      if(head[a] == head[b]) return numrev[a];
      b = par[head[b]];
    }
  }

  X query(int a, int b) {
    X L = def, R = def;
    a += n - 1, b += n - 1;
    while(a < b) {
      if(a & 1) L = operation(L, dat[a++]);
      if(b & 1) R = operation(dat[--b], R);
      a >>= 1, b >>= 1;
    }  
    return  operation(L, R);
  }

  ll get(int u, int v) {//uからvへのパス上の頂点についてやる
    int w = num[getlca(u, v)];//lcaで左右に分ける
    u = num[u];
    v = num[v];
    X L = def;
    while(u != w) {
      int hed = max<int>(head[u], w+1);
      L = operation(query(hed, u+1), L); 
      if(hed != w)  u = par[hed];
      else break;
    }   
    L = operation(query(w, w+1), L);
    X R = def;
    while(v != w) {
      int hed = max<int>(head[v], w+1);
      R = operation(query(hed, v+1), R); 
      if(hed != w)  v = par[hed];
      else break;
    }
    R = operation(query(w, w+1), R);
    //R = operation(query(1, size+1, w, w+1, 1), R); これをしてないので、RはまだLCAに辿り着いてない。
    return L.s + R.s;//交換則を要する時はこの行を変更する必要があるかもしれない : 無いと思うが
  }


  int parent(int a) {
    return numrev[par[num[a]]];
  }
};



void solve() {
  ll N;
  cin >> N >> K;
  vvl G(N+1);
  map<P, ll> w;
  rep(i,1,N-1) {
    ll a, b;
    cin >> a >> b; a++, b++;
    G[a].push_back(b);
    G[b].push_back(a);
    ll c;
    cin >> c;
    w[P(a, b)] = c;
  }

  
  HL<XX> hld(N, G);

  for(auto [p, c] : w) {
    auto [a, b] = p;
    if(hld.parent(a) == b) swap(a, b);
    hld.change(b, c, 0);
  }
  ll Q;
  cin >> Q;
  rep(qi, 1, Q) {
    string type;
    cin >> type;
    if(type == "add") {
      ll x, d;
      cin >> x >> d;
      x++;
      hld.change(x, d, 1);
    }
    else {
      ll s, t;
      cin >> s >> t; s++, t++;
      cout << hld.get(s, t) << endl;
    }
  }
 

    

}
//無断で0を平方数にカウントする人もいる
//”部分文字列”と”連続部分文字列”は違うので確認すること
//一般のグラフと、有向辺かつその貼り方に制約がある(多くの場合：番号がで解放に伸びる）はだいぶ違うので確認すること
//座標を2で割った時の”切り捨て側(左側）”を求めるには 誤:(x / 2) マイナスの時！！！ 正:floor<ll>(x, 2);
//stringでの数字の下から1桁目は 正:S.at(N-1)  誤:S.at(0)
//if(S.at(i) == 1)　← charなのに1...?
// modは取りましたか...?(´・ω・｀)
//sortの比較関数は、 a == b　ならば falseを返す必要がある（そうで無いとRE(発生しない場合もある））



int main() {
  ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  cout << fixed << setprecision(15);
    ll T = 1;
    //cin >> T;
    rep(i, 1, T) {
        solve();
    }
  return 0;
}
/*
3 100
0 1 1
1 2 1
2
send 0 2
send 2 0

*/
accuracy: 1
time_ms: 190
memory_kb: 34240
final_rank: 5

code_snippet: #include<bits/stdc++.h>
#define PPque priority_queue<tuple<ll, ll, ll>, vector<tuple<ll, ll, ll>>, greater<tuple<ll, ll, ll>>>
#define Pque  priority_queue<pair<ll, ll>, vector<pair<ll, ll>>, greater<pair<ll, ll>>>
#define pque priority_queue<ll, vector<ll>, greater<ll>>
#define umap unordered_map
#define uset unordered_set
#define rep(i, s, f) for(ll i = s; i <= f; i++)
#define per(i, s, f) for(ll i = s; i >= f; i--)
#define all0(x) (x).begin() ,(x).end()
#define all(x) (x).begin() + 1, (x).end()
#define vvvvl vector<vvvl>
#define vvvi vector<vector<vector<int>>>
#define vvvl vector<vector<vector<ll>>>
#define vvvc vector<vector<vector<char>>>
#define vvvd vector<vector<vector<double>>>
#define vvvm vector<vector<vector<mint>>>
#define vvi vector<vector<int>>
#define vvl vector<vector<ll>>
#define vvs vector<vector<string>>
#define vvc vector<vector<char>>
#define vvp vector<vector<pair<ll, ll>>>
#define vvb vector<vector<bool>>
#define vvd vector<vector<double>>
#define vvm vector<vector<mint>>
#define vp vector<pair<ll, ll>>
#define vi vector<int>
#define vl vector<ll>
#define vs vector<string>
#define vc vector<char>
#define vb vector<bool>
#define vd vector<double>
#define vm vector<mint>
#define P pair<ll, ll>
#define TU tuple<ll, ll, ll>
#define rrr(l, r) mt()%(r-l+1)+l
#define ENDL '\n'
#define ull unsigned long long
typedef long long ll;
using namespace std;

////////////////////////////////////////////////////////////////////////////////////////////////////////////
//これが本当の組み込み関数ってね（笑）

template <typename T>
T or_less(vector<T> &A, T x) { //x以下で最大要素の添字 前提: sort済み 存在しない: -1
  return distance(A.begin(), upper_bound(A.begin(), A.end(), x)-1);
}
template <typename T>
T under(vector<T> &A, T x) { //x未満の最大要素の添字　前提: sort済み 存在しない: -1
  return distance(A.begin(), lower_bound(A.begin(), A.end(), x)-1);
}
template <typename T>
T or_more(vector<T> &A, T x) { //x以上で最小要素の添字　　前提: sort済み 存在しない: N .   //distanceのA.beginは添字を出すために常にA.begin() NG: A.begin() + 1
  return distance(A.begin(), lower_bound(A.begin(), A.end(), x));
}
template <typename T>
T over(vector<T> &A, T x) { //xより大きい最小要素の添字前提: sort済み 存在しない: N
  return distance(A.begin(), upper_bound(A.begin(), A.end(), x));
}
template <typename T>
vector<T> vec_shift_right(vector<T> A, ll step, ll dir = 1, ll indexed = 1) {//dir = 1 : 右シフト  dir = -1 : 左シフト
  ll N = A.size() - indexed;
  vector<T> res(N+1);
  rep(i, indexed, N) {
    ll idx = i - step * dir;
    if(idx < indexed) idx += N;
    if(idx > N) idx -= N;
    res.at(i) = A.at(idx);
  }
  return res;
}
template <typename T>
void UNIQUE(vector<T> &A) {
    sort(all0(A));
  return A.erase(unique(A.begin(), A.end()), A.end());
}
template <typename T>
void rev90(vector<vector<T>> &A, int indexed = 1) {
  reverse(A.begin() + indexed, A.end());
  int n = A.size();
  rep(i, indexed, n-1) {
   rep(j, i+1, n-1) {
      swap(A.at(i).at(j), A.at(j).at(i));
    }
  }
}
void chmin(ll &a, ll b) {
  a = min(a, b);
}
void chmax(ll &a, ll b) {
  a = max(a, b);
}
//////////////////////////////////////////////////////////////////////
//数学系
///////////////////////////////////////////////////////////////////////
ll round(ll x, ll i) {return ll(x + 5 * pow(10, i-1))/ll(pow(10, i)) * ll(pow(10, i));}
vp insu_bunkai(ll N) {
  vp res;
  for (ll i = 2; i * i <= N; i++) {
    ll cnt = 0;
    while(N % i == 0) {
      cnt++;
      N /= i;
    }
    if(cnt != 0) res.push_back(P(i, cnt));
  }
  if(N != 1) res.push_back(P(N, 1));
  return res;
}
ll extgcd (ll a, ll b,  ll &x, ll &y) {
  if(b == 0) { x = 1;y = 0;return a;}
  ll d = extgcd(b, a%b, y, x);
  y -= a/b * x;
  return d;
}
template <typename T>
T ceil(T a, T b) {
  assert(b != 0);
  if(a % b == 0) return a / b;
  if((a <= 0 && b < 0) || (a >= 0 && b > 0)) return a/b + 1;
  else return a / b;
}
template <typename T>
T floor(T a, T b) {
  assert(b != 0);
  if(a % b == 0) return a / b;
  if((a <= 0 && b < 0) || (a >= 0 && b > 0)) return a/b;
  else return a/b - 1;
}
ll modpow(ll x, ll y, ll mod) {
  if(x > mod) x %= mod;
  if(y == 0) return 1;
  ll res = modpow(x, y >> 1, mod);
  res = res * res % mod;
  if(y & 1) res *= x, res %= mod;
  return res;
}
ll sqrt_(ll a) {
    ll l = 0;
    ll r = 3000000000LL;
    while(l < r) {
        ll mid = (l + r + 1) / 2;
        if(mid * mid <= a) l = mid;
        else r = mid - 1;
    }
    return l;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//グローバル変数を置くところ（情報工学意識高め）
//#pragma GCC optimize("O3")
//#pragma GCC optimize("unroll-loops")
const ll int_max = 1001001001;
const ll ll_max = 1001001001001001001LL;
const double pi = 3.141592653589793;
vl dx{0, 1, 0, -1, 0, 1, 1, -1, -1};  // (番兵) → ↓ ← ↑  ※ 右から時計回り　
vl dy{0, 0, -1, 0, 1, 1, -1, -1, 1};
//const ll mod = 1000000007;
//const ll mod = 998244353;
ll K;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct lca_getter {
  int N;
  vvl dp;
  vi depth;
  ll n = 0;
  lca_getter(){}
  void build(int _N, vvl& G)  {
    N = _N;
    ll c_n = N;
    while(c_n > 0) {
      c_n >>= 1;
      n++;
    }
    //n回登れば超えるということは, 2^n-1の桁までの合計で表せる

    dp.resize(n);
    depth.resize(N+1, 0);
    rep(i,0,n-1)dp[i].resize(N+1);
    auto dfs = [&](auto dfs, ll now, ll prev) -> void {
      if(prev != -1) depth[now] = depth[prev] + 1;
      for(ll to : G[now]) {
        if(to == prev) continue;
        dp[0][to] = now;
        dfs(dfs, to, now);
      }
    };
    dfs(dfs, 1, -1);
    dp[0][1] = -1;
    rep(i, 1, n-1) {
      rep(j, 1, N) {
        if(dp[i-1][j] == -1) dp[i][j] = -1;
        else dp[i][j] = dp[i-1][dp[i-1][j]];
      }
    }
  }
  int get(int a, int b) {
    if(depth[a] < depth[b]) swap(a, b);
    if(depth[a] > depth[b]) {
      int diff = depth[a] - depth[b];
      rep(i, 0, n-1) {
        if(diff >> i & 1) a = dp[i][a];
      }
    }
    if(a == b) return a;
    per(i, n-1, 0) {
      if(dp[i][a] != dp[i][b]) a = dp[i][a], b = dp[i][b];
    }
    return dp[0][a];
  }
};



/////////////////////////////////////////////////////////////////////////////////////////////////////////
//いじるところ。
lca_getter LCA;//solveの中でbuildを呼べば良い。
struct XX {
  bool e = false; 
  ll l = 0, r = 0, s = 0;
  XX(){e = true;}
  XX(ll _l, ll _r, ll _s) : l(_l), r(_r), s(_s){e = false;}
};
XX def;
XX gen(0, 0, 0);
XX operation(XX a, XX b) {
  if(a.e) return b;
  if(b.e) return a;
  XX res(0, 0, 0);
  res.l = a.l;
  res.r = b.r;
  res.s = a.s + b.s + a.r + b.l;
  if((a.r + b.l)%K == 0) res.s -= a.r + b.l;
  return res;
}

XX update(XX a, ll b, int t) {
  XX res = a;
  res.l += b;
  if(t == 1)res.r += b;
  return res;
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////

template <typename X>
struct HL {
  vector<int> siz;//[元の頂点番号] = サイズ
  vector<int> num;//[元の頂点番号] = 振り直した頂点番号
  vector<int> numrev;
  vector<int> par;//[振られた] = 振られた 自分の親
  vector<int> head;//[振られた番号] = 振られた番号 自分の連結成分の頭
  int N;
  vector<X> dat;//セグ木用の配列、0...上り 1...下り
  
  int n = 1;
  

  HL(int _N, vvl& G): N(_N) {
    par.resize(N+1);
    iota(par.begin(), par.end(), 0);
    siz.resize(N+1, 1);
    num.assign(N+1, -1);
    numrev.resize(N+1, -1);
    head.resize(N+1);
    while(n < N) n <<= 1;
    dat.assign(n*2, gen);

    auto dfs_siz = [&](auto dfs_siz, int now, int prev) -> void {
      int sum = 1;
      for(int to : G[now]) {
        if(to == prev) continue;
         dfs_siz(dfs_siz, to, now);
         sum += siz[to];
      }
      siz[now] = sum;
      return;
    };
    dfs_siz(dfs_siz, 1, -1);
    rep(i, 1, N){
      sort(G[i].begin(), G[i].end(), [&](int a, int b) {
        return siz[a] > siz[b];
      });
    }

    int idx = 1;
    auto dfs_bunkai = [&](auto dfs_bunkai, int now, int prev, int hed) -> void {
      num[now] = idx;//番号付
      numrev[idx] = now;
      idx++;
      par[num[now]] = prev;//親の頂点   //1だけは直前も自分も1
      if(hed == -1)hed = num[now];
      head[num[now]] = hed;

      bool flag = true;
      
      rep(i, 0, int(G[now].size()) - 1) {
        if(num[G[now][i]] != -1) continue;
        if(flag) dfs_bunkai(dfs_bunkai, G[now][i], num[now], hed), flag = false;
        else dfs_bunkai(dfs_bunkai, G[now][i], num[now], -1);
      }
      return;
    };
    dfs_bunkai(dfs_bunkai, 1, 1, -1);
  }
  // 振り終わった
  //////////////////////////////////////////
  
  
  void change(int pos, ll x, int t) {
    pos = num[pos];//振り当てた番号で扱う！！！
    pos = n + pos - 1;
    dat[pos] = update(dat[pos], x, t);
    while(pos >= 2) {
      pos >>= 1;
      dat[pos] = operation(dat[pos * 2], dat[pos * 2 + 1]);
    }
  }

  X query(int a, int b) {
    X L = def, R = def;
    a += n - 1, b += n - 1;
    while(a < b) {
      if(a & 1) L = operation(L, dat[a++]);
      if(b & 1) R = operation(dat[--b], R);
      a >>= 1, b >>= 1;
    }  
    return  operation(L, R);
  }

  ll get(int u, int v) {//uからvへのパス上の頂点についてやる
    int w = num[LCA.get(u, v)];//lcaで左右に分ける
    u = num[u];
    v = num[v];
    X L = def;
    while(u != w) {
      int hed = max<int>(head[u], w+1);
      L = operation(query(hed, u+1), L); 
      if(hed != w)  u = par[hed];
      else break;
    }   
    L = operation(query(w, w+1), L);
    X R = def;
    while(v != w) {
      int hed = max<int>(head[v], w+1);
      R = operation(query(hed, v+1), R); 
      if(hed != w)  v = par[hed];
      else break;
    }
    R = operation(query(w, w+1), R);
    //R = operation(query(1, size+1, w, w+1, 1), R); これをしてないので、RはまだLCAに辿り着いてない。
    return L.s + R.s;//交換則を要する時はこの行を変更する必要があるかもしれない : 無いと思うが
  }


  int parent(int a) {
    return numrev[par[num[a]]];
  }
};



void solve() {
  ll N;
  cin >> N >> K;
  vvl G(N+1);
  map<P, ll> w;
  rep(i,1,N-1) {
    ll a, b;
    cin >> a >> b; a++, b++;
    G[a].push_back(b);
    G[b].push_back(a);
    ll c;
    cin >> c;
    w[P(a, b)] = c;
  }

  
  
  LCA.build(N, G);
  HL<XX> hld(N, G);

  for(auto [p, c] : w) {
    auto [a, b] = p;
    if(hld.parent(a) == b) swap(a, b);
    hld.change(b, c, 0);
  }
  ll Q;
  cin >> Q;
  rep(qi, 1, Q) {
    string type;
    cin >> type;
    if(type == "add") {
      ll x, d;
      cin >> x >> d;
      x++;
      hld.change(x, d, 1);
    }
    else {
      ll s, t;
      cin >> s >> t; s++, t++;
      cout << hld.get(s, t) << endl;
    }
  }
 

    

}
//無断で0を平方数にカウントする人もいる
//”部分文字列”と”連続部分文字列”は違うので確認すること
//一般のグラフと、有向辺かつその貼り方に制約がある(多くの場合：番号がで解放に伸びる）はだいぶ違うので確認すること
//座標を2で割った時の”切り捨て側(左側）”を求めるには 誤:(x / 2) マイナスの時！！！ 正:floor<ll>(x, 2);
//stringでの数字の下から1桁目は 正:S.at(N-1)  誤:S.at(0)
//if(S.at(i) == 1)　← charなのに1...?
// modは取りましたか...?(´・ω・｀)
//sortの比較関数は、 a == b　ならば falseを返す必要がある（そうで無いとRE(発生しない場合もある））



int main() {
  ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  cout << fixed << setprecision(15);
    ll T = 1;
    //cin >> T;
    rep(i, 1, T) {
        solve();
    }
  return 0;
}
/*
3 100
0 1 1
1 2 1
2
send 0 2
send 2 0

*/
accuracy: 1
time_ms: 250
memory_kb: 47848
final_rank: 15

code_snippet: #include <bits/stdc++.h>
using namespace std;

class fenwicktree {
private:
	int n;
	vector<long long> cur, val;
	long long sum(int r) const {
		long long ans = 0;
		for (int i = r; i >= 1; i -= i & (-i)) {
			ans += val[i];
		}
		return ans;
	}
public:
	fenwicktree() : n(0), cur(vector<long long>()), val(vector<long long>()) {}
	fenwicktree(int n_) : n(n_), cur(vector<long long>(n_)), val(vector<long long>(n_ + 1)) {}
	void update(int pos, long long x) {
		x -= cur[pos];
		for (int i = pos + 1; i <= n; i += i & (-i)) {
			val[i] += x;
		}
		cur[pos] += x;
	}
	long long sum(int l, int r) const {
		return sum(r) - sum(l);
	}
};

struct edge {
	int to; long long cost;
};

int main() {
	// step #1. input of tree
	cin.tie(0);
	ios::sync_with_stdio(false);
	int N; long long K;
	cin >> N >> K;
	vector<vector<edge> > G(N);
	for (int i = 0; i < N - 1; i++) {
		int a, b, c;
		cin >> a >> b >> c;
		G[a].push_back(edge{b, c});
		G[b].push_back(edge{a, c});
	}

	// step #2. ordering
	vector<int> subsize(N);
	vector<edge> par(N, edge{-1, 0LL});
	vector<vector<int> > child(N);
	auto ordering = [&](auto& self, int pos) -> void {
		for (edge e : G[pos]) {
			if (e.to != par[pos].to) {
				child[pos].push_back(e.to);
				par[e.to] = edge{pos, e.cost};
			}
		}
		subsize[pos] = 1;
		for (int& v : child[pos]) {
			self(self, v);
			subsize[pos] += subsize[v];
			if (subsize[v] > subsize[child[pos][0]]) {
				swap(v, child[pos][0]);
			}
		}
	};
	ordering(ordering, 0);

	// step #3. build tree
	int counter = 0;
	vector<int> ord(N);
	vector<int> head(N);
	auto build_tree = [&](auto& self, int pos) -> void {
		ord[pos] = counter++;
		for (int v : child[pos]) {
			head[v] = (v == child[pos][0] ? head[pos] : v);
			self(self, v);
		}
	};
	build_tree(build_tree, 0);
	vector<int> upper(N, -1);
	vector<int> lower(N, -1);
	for (int i = 0; i < N; i++) {
		if (!child[i].empty()) {
			upper[child[i][0]] = i;
			lower[i] = child[i][0];
		}
	}

	// step #4. heavy-light decomposition
	vector<long long> cv(N), ce(N);
	fenwicktree bit(N);
	auto f = [&](long long x) -> long long {
		return x % K != 0 ? x : 0;
	};
	auto query = [&](int va, int vb) -> long long {
		long long ans = 0;
		while (head[va] != head[vb]) {
			if (ord[va] > ord[vb]) {
				swap(va, vb);
			}
			ans += bit.sum(ord[head[vb]] + 1, ord[vb] + 1);
			vb = head[vb];
			ans += f(cv[vb] + cv[par[vb].to] + par[vb].cost);
			vb = par[vb].to;
		}
		if (ord[va] > ord[vb]) {
			swap(va, vb);
		}
		ans += bit.sum(ord[va] + 1, ord[vb] + 1);
		return ans;
	};
	
	// step #5. initialization
	auto change = [&](int pos) -> void {
		if (upper[pos] != -1) {
			bit.update(ord[pos], f(cv[pos] + cv[upper[pos]] + par[pos].cost));
		}
		if (lower[pos] != -1) {
			bit.update(ord[lower[pos]], f(cv[pos] + cv[lower[pos]] + par[lower[pos]].cost));
		}
	};
	for (int i = 0; i < N; i++) {
		change(i);
	}

	// step #6. process query
	int Q;
	cin >> Q;
	for (int id = 1; id <= Q; id++) {
		string s; int x, y;
		cin >> s >> x >> y;
		if (s == "send") {
			long long ans = query(x, y);
			cout << ans << '\n';
		}
		if (s == "add") {
			cv[x] += y;
			change(x);
		}
	}

	return 0;
}
accuracy: 1
time_ms: 70
memory_kb: 32888
final_rank: 2

code_snippet: #include <bits/stdc++.h>
using namespace std;
struct monoid{
  bool e;
  long long L, R, S;
  monoid(): e(true){
  }
  monoid(long long x): e(false), L(x), R(x), S(0){
  }
};
monoid f(monoid A, monoid B, int K){
  if (A.e){
    return B;
  }
  if (B.e){
    return A;
  }
  monoid ans;
  ans.e = false;
  ans.L = A.L;
  ans.R = B.R;
  ans.S = A.S + B.S;
  if ((A.R + B.L) % K != 0){
    ans.S += A.R + B.L;
  }
  return ans;
};
struct segment_tree{
  int N;
  array<vector<monoid>, 2> ST;
  int K;
  segment_tree(){
  }
  segment_tree(vector<long long> &A, int K): K(K){
    int N2 = A.size();
    N = 1;
    while (N < N2){
      N *= 2;
    }
    ST[0] = vector<monoid>(N * 2 - 1);
    ST[1] = vector<monoid>(N * 2 - 1);
    for (int i = 0; i < N2; i++){
      ST[0][N - 1 + i] = monoid(A[i]);
      ST[1][N - 1 + i] = monoid(A[i]);
    }
    for (int i = N - 2; i >= 0; i--){
      ST[0][i] = f(ST[0][i * 2 + 1], ST[0][i * 2 + 2], K);
      ST[1][i] = f(ST[1][i * 2 + 2], ST[1][i * 2 + 1], K);
    }
  }
  void update(int i, int x){
    i += N - 1;
    ST[0][i].L += x;
    ST[0][i].R += x;
    ST[1][i].L += x;
    ST[1][i].R += x;
    while (i > 0){
      i = (i - 1) / 2;
      ST[0][i] = f(ST[0][i * 2 + 1], ST[0][i * 2 + 2], K);
      ST[1][i] = f(ST[1][i * 2 + 2], ST[1][i * 2 + 1], K);
    }
  }
  monoid range_fold(int L, int R, int d, int i, int l, int r){
    if (r <= L || R <= l){
      return monoid();
    } else if (L <= l && r <= R){
      return ST[d][i];
    } else {
      int m = (l + r) / 2;
      if (d == 0){
        return f(range_fold(L, R, d, i * 2 + 1, l, m), range_fold(L, R, d, i * 2 + 2, m, r), K);
      } else {
        return f(range_fold(L, R, d, i * 2 + 2, m, r), range_fold(L, R, d, i * 2 + 1, l, m), K);
      }
    }
  }
  monoid range_fold(int L, int R, int d){
    return range_fold(L, R, d, 0, 0, N);
  }
};
struct heavy_light_decomposition{
  vector<int> p, sz, in, next;
  segment_tree ST;
  int K;
  heavy_light_decomposition(vector<int> &p, vector<vector<int>> &c, vector<long long> &A, int K): p(p), K(K){
    int N = p.size();
    sz = vector<int>(N, 1);
    dfs1(c);
    in = vector<int>(N);
    next = vector<int>(N);
    next[0] = 0;
    int t = 0;
    dfs2(c, t);
    vector<long long> A2(N);
    for (int i = 0; i < N; i++){
      A2[in[i]] = A[i];
    }
    ST = segment_tree(A2, K);
  }
  void dfs1(vector<vector<int>> &c, int v = 0){
    for (int &w : c[v]){
      dfs1(c, w);
      sz[v] += sz[w];
      if (sz[w] > sz[c[v][0]]){
        swap(w, c[v][0]);
      }
    }
  }
  void dfs2(vector<vector<int>> &c, int &t, int v = 0){
    in[v] = t;
    t++;
    for (int w : c[v]){
      if (w == c[v][0]){
        next[w] = next[v];
      } else {
        next[w] = w;
      }
      dfs2(c, t, w);
    }
  }
  void update(int v, int x){
    ST.update(in[v], x);
  }
  int lca(int u, int v){
    while (true){
      if (in[u] > in[v]){
        swap(u, v);
      }
      if (next[u] == next[v]){
        return u;
      }
      v = p[next[v]];
    }
  }
  monoid path_fold(int u, int v){
    int w = lca(u, v);
    monoid L;
    while (next[u] != next[w]){
      L = f(L, ST.range_fold(in[next[u]], in[u] + 1, 1), K);
      u = p[next[u]];
    }
    L = f(L, ST.range_fold(in[w], in[u] + 1, 1), K);
    monoid R;
    while (next[v] != next[w]){
      R = f(ST.range_fold(in[next[v]], in[v] + 1, 0), R, K);
      v = p[next[v]];
    }
    R = f(ST.range_fold(in[w] + 1, in[v] + 1, 0), R, K);
    return f(L, R, K);
  }
};
int main(){
  int N, K;
  cin >> N >> K;
  vector<vector<pair<int, int>>> E(N);
  for (int i = 0; i < N - 1; i++){
    int a, b, c;
    cin >> a >> b >> c;
    E[a].push_back(make_pair(c, b));
    E[b].push_back(make_pair(c, a));
  }
  vector<int> p(N, -1);
  vector<vector<int>> c(N);
  vector<long long> a(N);
  a[0] = 0;
  queue<int> q;
  q.push(0);
  while (!q.empty()){
    int v = q.front();
    q.pop();
    for (auto P : E[v]){
      int w = P.second;
      if (w != p[v]){
        p[w] = v;
        c[v].push_back(w);
        a[w] = P.first - a[v];
        q.push(w);
      }
    }
  }
  heavy_light_decomposition T(p, c, a, K);  
  int Q;
  cin >> Q;
  for (int i = 0; i < Q; i++){
    string S;
    cin >> S;
    if (S == "add"){
      int x, d;
      cin >> x >> d;
      T.update(x, d);
    }
    if (S == "send"){
      int s, t;
      cin >> s >> t;
      cout << T.path_fold(s, t).S << endl;
    }
  }
}
accuracy: 1
time_ms: 410
memory_kb: 41704
final_rank: 11

code_snippet: #include <bits/stdc++.h>
#define U [7 << 15] = {}
#define G )+((c=A[a]+A[P[a]]+C[a])%k?c:0)-M(
typedef long long L;
using namespace std;
map<L, L> T U;
L P U, E U, Z U, H U, C U, A U, O U, X U, B U, k, n, a, b, c;
char s U;
L M(L f) { return f ? B[f] + M(f ^ f & -f) : 0; }
L S(L a, L i) {
  for (a = M(i++ G i); i < 2e5; i += i & -i)
    B[i] += a;
}
L W(L f) {
  X[f] = n;
  S(f = H[f], O[f] = ++b);
  f &&W(f);
}
L D(L f) {
  L d;
  Z[f] = 1;
  for (map<L, L>::iterator I = T[f].begin(); I != T[f].end(); I++)
    d = I->first, !Z[d] ? P[d] = f, E[d] = E[f] + 1, C[d] = I->second, D(d),
    Z[f] += Z[d], !H[f] | Z[H[f]] < Z[d] ? H[f] = d : 0 : 0;
}
L K(L f) {
  a = X[f];
  n = X[b];
  return a - n ? M(O[E[a] < E[n] ? a = n, n = b, b = f, n : f] G O[a]) + K(P[a])
               : abs(M(O[f]) - M(O[b]));
}
main() {
  for (cin >> n >> k; --n; T[a][b] = T[b][a] = c)
    cin >> a >> b >> c;
  for (D(b = 0); n < *Z; n++)
    !b | H[P[n]] - n &&W(n);
  for (cin >> a; cin >> s >> a >> b; S(H[a], O[a]), S(a, O[a] - 1))
    *s & 2 ? cout << K(a) << endl, 0 : A[a] += b;
}
accuracy: 1
time_ms: 280
memory_kb: 38104
final_rank: 8

code_snippet: #include <bits/stdc++.h>
#define U [7 << 15] = {}
#define G )+((c=A[a]+A[P[a]]+C[a])%k?c:0)-M(
typedef long long L;
using namespace std;
map<L, L> T U;
L P U, E U, Z U, H U, C U, A U, O U, X U, B U, k, n, a, b, c;
char s U;
L M(L f) { return f ? B[f] + M(f ^ f & -f) : 0; }
L S(L a, L i) {
  for (a = M(i++ G i); i < 2e5; i += i & -i)
    B[i] += a;
}
L D(L f) {
  L d;
  Z[f] = 1;
  for (map<L, L>::iterator I = T[f].begin(); I != T[f].end(); I++)
    d = I->first, !Z[d] ? P[d] = f, E[d] = E[f] + 1, C[d] = I->second, D(d),
    Z[f] += Z[d], !H[f] | Z[H[f]] < Z[d] ? H[f] = d : 0 : 0;
}
L K(L f) {
  a = X[f];
  n = X[b];
  return a - n ? M(O[E[a] < E[n] ? a = n, n = b, b = f, n : f] G O[a]) + K(P[a])
               : abs(M(O[f]) - M(O[b]));
}
main() {
  for (cin >> n >> k; --n; T[a][b] = T[b][a] = c)
    cin >> a >> b >> c;
  for (D(a = b = 0); n < *Z; a = H[P[++n]] - n ? n : 0)
    for (; !b | a; S(a = H[a], O[a] = ++b))
      X[a] = n;
  for (cin >> a; cin >> s >> a >> b; S(H[a], O[a]), S(a, O[a] - 1))
    *s & 2 ? cout << K(a) << endl, 0 : A[a] += b;
}
accuracy: 1
time_ms: 280
memory_kb: 38100
final_rank: 7

code_snippet: #include <bits/stdc++.h>
   
using namespace std;
   
#define rep(i,n) for(int i=0;i<(n);++i)
#define reps(i,n) for(int i=1;i<=(n);++i)
#define all(x) begin(x), end(x)
#define Fixed fixed << setprecision(12)
#define int int32_t
using pii = pair<int,int>;
constexpr int32_t INF  = 0x3f3f3f3f;
constexpr int_fast64_t LINF = 0x3f3f3f3f3f3f3f3fLL;
constexpr int mod1 = 1e9+7;
constexpr int mod2 = 998244353;
   
template <class Func>
class FixPoint : Func {
public:
    explicit constexpr FixPoint(Func&& f) noexcept : Func(forward<Func>(f)) {}
   
    template <class... Args>
    constexpr decltype(auto) operator()(Args&&... args) const {
        return Func::operator()(*this, std::forward<Args>(args)...);
    }
};
   
template <class Func>
static inline constexpr decltype(auto) makeFixPoint(Func&& f) noexcept {
    return FixPoint<Func>{forward<Func>(f)};
}
   
template <class A, class B> inline bool chmax(A &a, const B &b) { return b > a && (a = b, true); }
template <class A, class B> inline bool chmin(A &a, const B &b) { return b < a && (a = b, true); }
   
template <class T> using max_heap = priority_queue<T>;
template <class T> using min_heap = priority_queue<T,vector<T>,greater<T> >;
template <class A, class B> using umap = unordered_map<A,B>;
   
template <class T> inline void bye(T x) { cout << x << '\n'; exit(0); }
   
inline int square(int a){ return a * a;}
inline int updiv(int a,int b){ return (a + b - 1) / b; }
   
constexpr int dx[] = {1,0,-1,0,1,1,-1,-1};
constexpr int dy[] = {0,-1,0,1,1,-1,-1,1};
   
struct HeavyLightDecomposition {
public:
    vector<vector<int> > g;
    vector<int> sz, in, out, head, rev, par, dep;
   
    HeavyLightDecomposition(const vector<vector<int> > &G) : g(G) {
        sz.resize(g.size(), 0);
        in.resize(g.size(), 0);
        out.resize(g.size(), 0);
        head.resize(g.size(), 0);
        rev.resize(g.size(), 0);
        par.resize(g.size(), 0);
        dep.resize(g.size(), 0);
        dfs_sz(0, -1, 0);
        dfs_hld(0, -1);
    }
   
    // 頂点vからk個遡った頂点
    int la(int v, int k) {
        while (true) {
            int u = head[v];
            if (in[v] - k >= in[u]) return (rev[in[v] - k]);
            k -= in[v] - in[u] + 1;
            v = par[u];
        }
    }
   
    int lca(int u, int v) const {
        for (;; v = par[head[v]]) {
            if (in[u] > in[v]) swap(u, v);
            if (head[u] == head[v]) return (u);
        }
    }
   
    int dist(int u, int v) const {
        return (dep[u] + dep[v] - 2 * dep[lca(u, v)]);
    }
   
    template<typename E, typename F, typename G, typename S>
    E query(int u, int v, const E &ti, const F &f, const G &g, const S &s, bool edge = false) {
        E l = ti, r = ti;
        for (;; v = par[head[v]]) {
            if (in[u] > in[v]) swap(u, v), swap(l, r);
            if (head[u] == head[v]) break;
            l = g(f(in[head[v]], in[v] + 1), l);
        }
        return (s(g(f(in[u] + edge, in[v] + 1), l), r));
    }
   
    template<typename E, typename F, typename G>
    E query(int u, int v, const E &ti, const F &f, const G &g, bool edge = false) {
        return (query(u, v, ti, f, g, g, edge));
    }
   
    template<typename G>
    void update(int u, int v, const G &g, bool edge = false) {
        for (;; v = par[head[v]]) {
            if (in[u] > in[v]) swap(u, v);
            if (head[u] == head[v]) break;
            g(in[head[v]], in[v] + 1);
        }
        g(in[u] + edge, in[v] + 1);
    }
   
    /* {parent, child} */
    vector<pair<int, int> > compress(vector<int> &remark) {
        auto comp = [&](int a, int b) { return (in[a] < in[b]); };
        sort(begin(remark), end(remark), comp);
        remark.erase(unique(begin(remark), end(remark)), end(remark));
        int K = (int) remark.size();
        for (int k = 1; k < K; ++k) remark.emplace_back(lca(remark[k - 1], remark[k]));
        sort(begin(remark), end(remark), comp);
        remark.erase(unique(begin(remark), end(remark)), end(remark));
        vector<pair<int, int> > es;
        vector<int> st;
        for (auto &k : remark) {
            while (!st.empty() && out[st.back()] <= in[k]) st.pop_back();
            if (!st.empty()) es.emplace_back(st.back(), k);
            st.emplace_back(k);
        }
        return (es);
    }
   
private:
    void dfs_sz(int cur, int p, int d) {
        dep[cur] = d;
        par[cur] = p;
        sz[cur] = 1;
        if (!g[cur].empty() && g[cur][0] == p) swap(g[cur][0], g[cur].back());
        for (auto &to : g[cur]) {
            if (to == p) continue;
            dfs_sz(to, cur, d + 1);
            sz[cur] += sz[to];
            if (sz[g[cur][0]] < sz[to]) swap(g[cur][0], to);
        }
    }
   
    void dfs_hld(int cur, int p) {
        static int times = 0;
        in[cur] = times++;
        rev[in[cur]] = cur;
        for (auto &to : g[cur]) {
            if (to == p) continue;
            head[to] = (g[cur][0] == to ? head[cur] : to);
            dfs_hld(to, cur);
        }
        out[cur] = times;
    }
};
   
template< typename Monoid, typename F >
struct SegmentTree {
    int n, sz;
    vector< Monoid > seg;
   
    const F f;
    const Monoid M1;
       
    SegmentTree(int n, const F f, const Monoid &M1) : n(n), f(f), M1(M1) {
        sz = 1;
        while(sz < n) sz <<= 1;
        seg.assign(2 * sz, M1);
    }
   
    void set(int k, const Monoid &x) {
        seg[k + sz] = x;
    }
   
    void build() {
        for(int k = sz - 1; k > 0; k--) {
            seg[k] = f(seg[2 * k + 0], seg[2 * k + 1]);
        }
    }
   
    void update(int k, const Monoid &x) {
        k += sz;
        seg[k] = x;
        while(k >>= 1) {
            seg[k] = f(seg[2 * k + 0], seg[2 * k + 1]);
        }
    }
   
    Monoid query(int a, int b) {
        Monoid L = M1, R = M1;
        for(a += sz, b += sz; a < b; a >>= 1, b >>= 1) {
            if(a & 1) L = f(L, seg[a++]);
            if(b & 1) R = f(seg[--b], R);
        }
        return f(L, R);
    }
   
    Monoid operator[](const int &k) const {
        return seg[k + sz];
    }
   
    template< typename C >
    int find_subtree(int a, const C &check, Monoid &M, bool type) {
        while(a < sz) {
            Monoid nxt = type ? f(seg[2 * a + type], M) : f(M, seg[2 * a + type]);
            if(check(nxt)) a = 2 * a + type;
            else M = nxt, a = 2 * a + 1 - type;
        }
        return a - sz;
    }
   
    template< typename C >
    int find_first(int a, const C &check) {
        Monoid L = M1;
        if(a <= 0) {
            if(check(f(L, seg[1]))) return find_subtree(1, check, L, false);
            return n;
        }
        int b = sz;
        for(a += sz, b += sz; a < b; a >>= 1, b >>= 1) {
            if(a & 1) {
                Monoid nxt = f(L, seg[a]);
                if(check(nxt)) return find_subtree(a, check, L, false);
                L = nxt;
                ++a;
            }
        }
        return n;
    }
   
    template< typename C >
    int find_last(int b, const C &check) {
        Monoid R = M1;
        if(b >= sz) {
            if(check(f(seg[1], R))) return find_subtree(1, check, R, true);
            return -1;
        }
        int a = sz;
        for(b += sz; a < b; a >>= 1, b >>= 1) {
            if(b & 1) {
                Monoid nxt = f(seg[--b], R);
                if(check(nxt)) return find_subtree(b, check, R, true);
                R = nxt;
            }
        }
        return -1;
    }
};
   
template< typename Monoid, typename F >
SegmentTree< Monoid, F > get_segment_tree(int N, const F& f, const Monoid& M1) {
    return {N, f, M1};
}
   
signed main(void){
    cin.tie(nullptr);
    ios_base::sync_with_stdio(false);
   
    int n, k;
    cin >> n >> k;
   
    vector<vector<int> > tree(n);
    vector<vector<pair<int, int> > > graph(n);
    map<pair<int, int>, int64_t> cost;
   
    for (int i = 0; i < n - 1; ++i) {
        int a, b, c;
        cin >> a >> b >> c;
        tree[a].emplace_back(b);
        tree[b].emplace_back(a);
        graph[a].emplace_back(b, c);
        graph[b].emplace_back(a, c);
        cost[minmax(a, b)] = c;
    }
   
    HeavyLightDecomposition hld(tree);
    auto seg = get_segment_tree(n, [](auto a, auto b) { return (a + b); }, 0LL);
    for (int i = 1; i < n; ++i) {
        int c = cost[minmax(hld.par[i], i)];
        if (c % k == 0) c = 0;
        seg.set(hld.in[i], c);
    }
    seg.build();
   
    int q;
    cin >> q;
   
    vector<int_fast64_t> sum(n);
    vector<int> idx(n);
    vector<tuple<string, int, int> > queries;
    unordered_set<int> us;
    int u, v, x, y, to, c;
    string com;
    for (int i = 0; i < q; ++i) {
        string com;
        cin >> com;
        if (com == "add") {
            int x, d;
            cin >> x >> d;
            // nodes.emplace_back(x);
            us.insert(x);
            queries.emplace_back(com, x, d);
        } else {
            int s, t;
            cin >> s >> t;
            // nodes.emplace_back(s);
            // nodes.emplace_back(t);
            us.insert(s);
            us.insert(t);
            queries.emplace_back(com, s, t);
        }
        if (i == q - 1 || us.size() >= 100) {
            vector<int> nodes;
            for (auto e: us) nodes.emplace_back(e);
            auto edges = hld.compress(nodes);
            int sz = nodes.size();
            for (int i = 0; i < sz; ++i) {
                idx[nodes[i]] = i;
            }
            vector<vector<int> > g(sz);
            for (int l = 0; l < edges.size(); ++l) {
                tie(u, v) = edges[l];
                g[idx[u]].emplace_back(idx[v]);
                g[idx[v]].emplace_back(idx[u]);
            }
   
            for (int l = 0; l < queries.size(); ++l) {
                tie(com, x, y) = queries[l];
                if (com == "add") {
                    sum[x] += y;
                } else {
                    vector<int> nv;
                    makeFixPoint([&](auto rec, auto cur, auto par) -> bool {
                        nv.emplace_back(nodes[cur]);
                        if (cur == idx[y]) {
                            return (true);
                        } else {
                            for (auto to: g[cur]) {
                                if (par == to) continue;
                                if (rec(to, cur)) return (true);
                            }
                        }
                        nv.pop_back();
                        return (false);
                    })(idx[x], -1);
   
                    int vs = nv.size();
                    int_fast64_t res = 0;
                    for (int j = 0; j < vs - 1; ++j) {
                        int u = nv[j], v = nv[j + 1]; 
                        if (hld.in[u] > hld.in[v]) swap(u, v);
                        auto f = [&](int a, int b) { return (seg.query(a, b)); };
                        auto g = [](int a, int b) { return (a + b); };
                        res += hld.query(u, v, 0LL, f, g, true);
                        int vh = hld.la(v, 1);
                        int vc = cost[minmax(v, vh)] + sum[v] + sum[vh];
                        res -= seg[hld.in[v]];
                        if (vc % k != 0) res += vc;
                        if (vh != u) {
                            int ul = hld.la(v, hld.dist(u, v) - 1);
                            int uc = cost[minmax(u, ul)] + sum[u] + sum[ul];
                            res -= seg[hld.in[ul]];
                            if (uc % k != 0) res += uc;
                        }
                    }
                    cout << res << '\n';
                }
            }
   
            for (auto e : nodes) {
                if (sum[e] == 0) continue;
                for (int l = 0; l < graph[e].size(); ++l) {
                    tie(to, c) = graph[e][l];
                    int u = e, v = to;
                    if (hld.in[u] > hld.in[v]) swap(u, v);
                    if ((cost[minmax(u, v)] += sum[e]) % k == 0) c = 0;
                    else c = cost[minmax(u, v)];
                    seg.update(hld.in[v], c);
                }
                sum[e] = 0;
            }
   
            queries.clear();
            us.clear();
        }
    }
   
    return (0);
}
accuracy: 1
time_ms: 2250
memory_kb: 41728
final_rank: 19

code_snippet: #include <bits/stdc++.h>
using namespace std;

int K;

template <typename T>
struct BinaryIndexedTree {
    int N;
    vector<T>bit;
    BinaryIndexedTree(){}
    BinaryIndexedTree(int x) {
        N = x;
        bit.resize(x+1);
    }
    void add(int x,T y) {
        x++;
        while(x <= N) {
            bit[x] += y;
            x += x&-x;
        }
    }
    T sum(int x) {
        x++;
        T res = 0;
        while(x) {
            res += bit[x];
            x -= x&-x;
        }
        return res;
    }
    inline T sum(int l, int r) {//[l,r)
        return sum(r-1)-sum(l-1);
    }
    inline T operator[](int k) {
        return sum(k)-sum(k-1);
    }
};

struct HLD { // 根が 0 でない時に注意
    vector<int>p,sz,in,nxt;
    vector<long long>s,tr;
    vector<vector<int>>h;
    BinaryIndexedTree<long long> bit;
    void dfs1(vector<vector<int>>&c,int v = 0) {
        sz[v] = 1;
        for(int &w:c[v]) {
            dfs1(c,w);
            sz[v] += sz[w];
            if(sz[w] > sz[c[v][0]]) {
                swap(w,c[v][0]);
            }
        }
    }
    void dfs2(vector<vector<int>>&c,int &t,int v = 0) {
        in[v] = t;
        t++;
        for(int w:c[v]) {
            if(w == c[v][0]) {
                nxt[w] = nxt[v];
                h[v].push_back(w);
                h[w].push_back(v);
            }
            else {
                nxt[w] = w;
            }
            dfs2(c,t,w);
        }
    }
    HLD(vector<long long>&A,vector<int>&p,vector<vector<int>>&c):p(p) {
        int N = A.size();
        sz.resize(N,0);
        dfs1(c);
        in.resize(N,0);
        tr.resize(N,0);
        nxt.resize(N,0);
        h.resize(N);
        s.resize(N);
        int t = 0;
        dfs2(c,t);
        vector<long long>tmp(N);
        for(int i = 0; i < N; i++) {
            tr[i] = A[i];
            if(A[i]%K == 0) A[i] = 0;
            tmp[in[i]] = A[i];
        }
        BinaryIndexedTree<long long>init(tmp.size());
        for(int i = 0; i < tmp.size(); i++) {
            init.add(i,tmp[i]);
        }
        bit = init;
    }
    int lca(int u,int v) {
        while(true) {
            if(in[u] > in[v]) {
                swap(u,v);
            }
            if(nxt[u] == nxt[v]) {
                return u;
            }
            v = p[nxt[v]];
        }
    }
    long long diff(int x,long long d) {
        long long now = bit[in[x]];
        long long nxt = tr[x]+d;
        if(nxt%K == 0) nxt = 0;
        return nxt-now;
    }
    void update(int v,long long x) {
        s[v] += x;
        for(int i:h[v]) {
            if(i == p[v]) {
                bit.add(in[v],diff(v,x));
                tr[v] += x;
            }
            else {
                bit.add(in[i],diff(i,x));
                tr[i] += x;
            }
        }
    }
    long long query(int u,int v) {
        int w = lca(u,v);
        long long ans = 0;
        while(nxt[u] != nxt[w]) {
            ans += bit.sum(in[nxt[u]],in[u]+1);
            ans += diff(nxt[u],s[p[nxt[u]]]+s[nxt[u]]);
            u = p[nxt[u]];
        }
        ans += bit.sum(in[w]+1,in[u]+1);
        while(nxt[v] != nxt[w]) {
            ans += bit.sum(in[nxt[v]],in[v]+1);
            ans += diff(nxt[v],s[p[nxt[v]]]+s[nxt[v]]);
            v = p[nxt[v]];
        }
        ans += bit.sum(in[w]+1,in[v]+1);
        return ans;
    }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int N;
    cin >> N >> K;
    vector<vector<pair<int,int>>>ki(N);
    for(int i = 0; i < N-1; i++) {
        int a,b,c;
        cin >> a >> b >> c;
        ki[a].push_back({b,c});
        ki[b].push_back({a,c});
    }
    vector<long long>a(N);
    vector<int>p(N,-1);
    vector<vector<int>>c(N);
    queue<int>que;
    que.push(0);
    while (!que.empty()) {
        int x = que.front();
        que.pop();
        for(auto i:ki[x]) {
            if(i.first != 0 && p[i.first] == -1) {
                a[i.first] = i.second;
                p[i.first] = x;
                c[x].push_back(i.first);
                que.push(i.first);
            }
        }
    }
    HLD hld(a,p,c);
    int q;
    cin >> q;
    while(q--) {
        string s;
        cin >> s;
        if(s == "add") {
            int x,d;
            cin >> x >> d;
            hld.update(x,d);
        }
        else {
            int s,t;
            cin >> s >> t;
            cout << hld.query(s,t) << "\n";
        }
    }
}
accuracy: 1
time_ms: 90
memory_kb: 33952
final_rank: 3

code_snippet: #pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;

struct monoid{
    bool e;
    ll L,R,S;
    monoid() : e(true) {}
    monoid(ll x) : e(false), L(x), R(x), S(0) {}
};
struct segtree{
    int K;
    using S = monoid;
    S op(S a,S b) {
        if(a.e) return b;
        if(b.e) return a;
        S res;
        res.e = false;
        res.L = a.L; res.R = b.R;
        res.S = a.S + b.S;
        if((a.R + b.L) % K){
            res.S += (a.R + b.L);
        }
        return res;
    }
    S e() {return monoid();}
    int n;
    array<vector<S>, 2> tree;
    segtree() {}
    segtree(int nn){
        int lg = 0;
        while((1LL<<lg) < nn) lg++;
        n = 1<<lg;
        tree[0] = vector<S>(n*2,e());
        tree[1] = vector<S>(n*2,e());
    }
    void init(vector<ll> a){
        for (int i = 0; i < a.size(); ++i) {
            tree[0][n+i] = monoid(a[i]);
            tree[1][n+i] = monoid(a[i]);
        }
        for (int i = n-1; i > 0; i--) {
            tree[0][i] = op(tree[0][i*2], tree[0][i*2+1]);
            tree[1][i] = op(tree[1][i*2+1], tree[1][i*2]);
        }

    }
    void update(int pos,int val){
        pos += n;
        for (int i = 0; i < 2; ++i) {
            tree[i][pos].L += val;
            tree[i][pos].R += val;
        }
        while(pos > 1){
            int chl = pos, chr = pos^1;
            if(chl > chr) swap(chl, chr);
            tree[0][pos/2] = op(tree[0][chl], tree[0][chr]);
            tree[1][pos/2] = op(tree[1][chr], tree[1][chl]);
            pos /= 2;
        }
    }
    S query(int l,int r,int d){
        S sml = e(), smr = e();
        l += n; r += n;
        if(d == 0){
            for(;l < r; l >>= 1, r >>= 1){
                if(l%2) sml = op(sml, tree[0][l++]);
                if(r%2) smr = op(tree[0][--r], smr);
            }
        }
        else{
            for(;l < r; l >>= 1, r >>= 1){
                if(r%2) sml = op(sml, tree[1][--r]);
                if(l%2) smr = op(tree[1][l++], smr);
            }
        }
        return op(sml,smr);
    }
};

struct HLD{
    vector<int> p, sz, in, next;
    vector<vector<int>> c;
    segtree seg;
    HLD(vector<int> &p, vector<vector<int>> &c, vector<ll> a, int K) : p(p), c(c){
        int n = p.size();
        sz = vector<int>(n,1);
        dfs1(0);
        in = vector<int>(n);
        next = vector<int>(n);
        next[0] = 0;
        int id = 0;
        dfs2(id, 0);
        seg = segtree(n);
        seg.K = K;
        vector<ll> na(n);
        for (int i = 0; i < n; ++i) {
            na[in[i]] = a[i];
        }
        seg.init(na);
    }
    void dfs1(int s){
        for(int &t:c[s]){
            dfs1(t);
            sz[s] += sz[t];
            if(sz[t] > sz[c[s][0]]){
                swap(t, c[s][0]);
            }
        }
    }
    void dfs2(int &id, int s){
        in[s] = id++;
        for(int t:c[s]){
            if(t == c[s][0]){
                next[t] = next[s];
            }
            else{
                next[t] = t;
            }
            dfs2(id, t);
        }
    }
    int lca(int s,int t){
        while(1){
            if(in[s] > in[t]){
                swap(s,t);
            }
            if(next[s] == next[t]){
                return s;
            }
            t = p[next[t]];
        }
    }
    void update(int s,int x){
        seg.update(in[s], x);
    }
    monoid path_fold(int s,int t){
        int w = lca(s,t);
        monoid L,R;
        while(next[s] != next[w]){
            L = seg.op(L, seg.query(in[next[s]], in[s]+1, 1));
            s = p[next[s]];
        }
        auto r = seg.query(in[w], in[s]+1, 1);
        L = seg.op(L, seg.query(in[w], in[s]+1, 1));
        while(next[t] != next[w]){
            R = seg.op(seg.query(in[next[t]], in[t]+1, 0), R);
            t = p[next[t]];
        }
        R = seg.op(seg.query(in[w]+1, in[t]+1, 0), R);
        return seg.op(L,R);
    }
};

int main(){
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    int n,k; cin >> n >> k;
    vector<vector<pair<int,int>>> g(n);
    for (int i = 0; i < n - 1; ++i) {
        int a,b,c; cin >> a >> b >> c;
        g[a].push_back({b,c});
        g[b].push_back({a,c});
    }
    vector<int> p(n,-1);
    vector<vector<int>> c(n);
    vector<ll> a(n);
    a[0] = 0;
    queue<int> q;
    q.push(0);
    while(q.size()){
        int s = q.front(); q.pop();
        for(auto to:g[s]){
            int t = to.first;
            if(p[s] == t) continue;
            p[t] = s;
            c[s].push_back(t);
            a[t] = to.second - a[s];
            q.push(t);
        }
    }
    HLD T(p,c,a,k);
    auto seg = T.seg;
    int query; cin >> query;
    while(query--){
        string s; cin >> s;
        if(s == "add"){
            int x,d; cin >> x >> d;
            T.update(x, d);
        }
        else{
            int s,t; cin >> s >> t;
            cout << T.path_fold(s, t).S << "\n";
        }
    }
}
accuracy: 1
time_ms: 90
memory_kb: 61120
final_rank: 17

code_snippet: #include <bits/stdc++.h>
using namespace std;
struct monoid{
  bool e;
  long long L, R, S;
  monoid(): e(true){
  }
  monoid(long long x): e(false), L(x), R(x), S(0){
  }
};
monoid f(monoid A, monoid B, int K){
  if (A.e){
    return B;
  }
  if (B.e){
    return A;
  }
  monoid ans;
  ans.e = false;
  ans.L = A.L;
  ans.R = B.R;
  ans.S = A.S + B.S;
  if ((A.R + B.L) % K != 0){
    ans.S += A.R + B.L;
  }
  return ans;
};
struct segment_tree{
  int N;
  array<vector<monoid>, 2> ST;
  int K;
  segment_tree(){
  }
  segment_tree(vector<long long> &A, int K): K(K){
    int N2 = A.size();
    N = 1;
    while (N < N2){
      N *= 2;
    }
    ST[0] = vector<monoid>(N * 2 - 1);
    ST[1] = vector<monoid>(N * 2 - 1);
    for (int i = 0; i < N2; i++){
      ST[0][N - 1 + i] = monoid(A[i]);
      ST[1][N - 1 + i] = monoid(A[i]);
    }
    for (int i = N - 2; i >= 0; i--){
      ST[0][i] = f(ST[0][i * 2 + 1], ST[0][i * 2 + 2], K);
      ST[1][i] = f(ST[1][i * 2 + 2], ST[1][i * 2 + 1], K);
    }
  }
  void update(int i, int x){
    i += N - 1;
    ST[0][i].L += x;
    ST[0][i].R += x;
    ST[1][i].L += x;
    ST[1][i].R += x;
    while (i > 0){
      i = (i - 1) / 2;
      ST[0][i] = f(ST[0][i * 2 + 1], ST[0][i * 2 + 2], K);
      ST[1][i] = f(ST[1][i * 2 + 2], ST[1][i * 2 + 1], K);
    }
  }
  monoid range_fold(int L, int R, int d, int i, int l, int r){
    if (r <= L || R <= l){
      return monoid();
    } else if (L <= l && r <= R){
      return ST[d][i];
    } else {
      int m = (l + r) / 2;
      if (d == 0){
        return f(range_fold(L, R, d, i * 2 + 1, l, m), range_fold(L, R, d, i * 2 + 2, m, r), K);
      } else {
        return f(range_fold(L, R, d, i * 2 + 2, m, r), range_fold(L, R, d, i * 2 + 1, l, m), K);
      }
    }
  }
  monoid range_fold(int L, int R, int d){
    return range_fold(L, R, d, 0, 0, N);
  }
};
struct heavy_light_decomposition{
  vector<int> p, sz, in, next;
  segment_tree ST;
  int K;
  heavy_light_decomposition(vector<int> &p, vector<vector<int>> &c, vector<long long> &A, int K): p(p), K(K){
    int N = p.size();
    sz = vector<int>(N, 1);
    dfs1(c);
    in = vector<int>(N);
    next = vector<int>(N);
    next[0] = 0;
    int t = 0;
    dfs2(c, t);
    vector<long long> A2(N);
    for (int i = 0; i < N; i++){
      A2[in[i]] = A[i];
    }
    ST = segment_tree(A2, K);
  }
  void dfs1(vector<vector<int>> &c, int v = 0){
    for (int &w : c[v]){
      dfs1(c, w);
      sz[v] += sz[w];
      if (sz[w] > sz[c[v][0]]){
        swap(w, c[v][0]);
      }
    }
  }
  void dfs2(vector<vector<int>> &c, int &t, int v = 0){
    in[v] = t;
    t++;
    for (int w : c[v]){
      if (w == c[v][0]){
        next[w] = next[v];
      } else {
        next[w] = w;
      }
      dfs2(c, t, w);
    }
  }
  void update(int v, int x){
    ST.update(in[v], x);
  }
  int lca(int u, int v){
    while (true){
      if (in[u] > in[v]){
        swap(u, v);
      }
      if (next[u] == next[v]){
        return u;
      }
      v = p[next[v]];
    }
  }
  monoid path_fold(int u, int v){
    int w = lca(u, v);
    monoid L;
    while (next[u] != next[w]){
      L = f(L, ST.range_fold(in[next[u]], in[u] + 1, 1), K);
      u = p[next[u]];
    }
    L = f(L, ST.range_fold(in[w], in[u] + 1, 1), K);
    monoid R;
    while (next[v] != next[w]){
      R = f(ST.range_fold(in[next[v]], in[v] + 1, 0), R, K);
      v = p[next[v]];
    }
    R = f(ST.range_fold(in[w] + 1, in[v] + 1, 0), R, K);
    return f(L, R, K);
  }
};
int main(){
  int N, K;
  cin >> N >> K;
  vector<vector<pair<int, int>>> E(N);
  for (int i = 0; i < N - 1; i++){
    int a, b, c;
    cin >> a >> b >> c;
    E[a].push_back(make_pair(c, b));
    E[b].push_back(make_pair(c, a));
  }
  vector<int> p(N, -1);
  vector<vector<int>> c(N);
  vector<long long> a(N);
  a[0] = 0;
  queue<int> q;
  q.push(0);
  while (!q.empty()){
    int v = q.front();
    q.pop();
    for (auto P : E[v]){
      int w = P.second;
      if (w != p[v]){
        p[w] = v;
        c[v].push_back(w);
        a[w] = P.first - a[v];
        q.push(w);
      }
    }
  }
  heavy_light_decomposition T(p, c, a, K);	
  int Q;
  cin >> Q;
  for (int i = 0; i < Q; i++){
    string S;
    cin >> S;
    if (S == "add"){
      int x, d;
      cin >> x >> d;
      T.update(x, d);
    }
    if (S == "send"){
      int s, t;
      cin >> s >> t;
      cout << T.path_fold(s, t).S << endl;
    }
  }
}
accuracy: 1
time_ms: 420
memory_kb: 41616
final_rank: 12

code_snippet: #include <bits/stdc++.h>
using namespace std;
struct monoid{
  bool e;
  long long L, R, S;
  monoid(): e(true){
  }
  monoid(long long x): e(false), L(x), R(x), S(0){
  }
};
monoid f(monoid A, monoid B, int K){
  if (A.e){
    return B;
  }
  if (B.e){
    return A;
  }
  monoid ans;
  ans.e = false;
  ans.L = A.L;
  ans.R = B.R;
  ans.S = A.S + B.S;
  if ((A.R + B.L) % K != 0){
    ans.S += A.R + B.L;
  }
  return ans;
};
struct segment_tree{
  int N;
  array<vector<monoid>, 2> ST;
  int K;
  segment_tree(){
  }
  segment_tree(vector<long long> &A, int K): K(K){
    int N2 = A.size();
    N = 1;
    while (N < N2){
      N *= 2;
    }
    ST[0] = vector<monoid>(N * 2 - 1);
    ST[1] = vector<monoid>(N * 2 - 1);
    for (int i = 0; i < N2; i++){
      ST[0][N - 1 + i] = monoid(A[i]);
      ST[1][N - 1 + i] = monoid(A[i]);
    }
    for (int i = N - 2; i >= 0; i--){
      ST[0][i] = f(ST[0][i * 2 + 1], ST[0][i * 2 + 2], K);
      ST[1][i] = f(ST[1][i * 2 + 2], ST[1][i * 2 + 1], K);
    }
  }
  void update(int i, int x){
    i += N - 1;
    ST[0][i].L += x;
    ST[0][i].R += x;
    ST[1][i].L += x;
    ST[1][i].R += x;
    while (i > 0){
      i = (i - 1) / 2;
      ST[0][i] = f(ST[0][i * 2 + 1], ST[0][i * 2 + 2], K);
      ST[1][i] = f(ST[1][i * 2 + 2], ST[1][i * 2 + 1], K);
    }
  }
  monoid range_fold(int L, int R, int d, int i, int l, int r){
    if (r <= L || R <= l){
      return monoid();
    } else if (L <= l && r <= R){
      return ST[d][i];
    } else {
      int m = (l + r) / 2;
      if (d == 0){
        return f(range_fold(L, R, d, i * 2 + 1, l, m), range_fold(L, R, d, i * 2 + 2, m, r), K);
      } else {
        return f(range_fold(L, R, d, i * 2 + 2, m, r), range_fold(L, R, d, i * 2 + 1, l, m), K);
      }
    }
  }
  monoid range_fold(int L, int R, int d){
    return range_fold(L, R, d, 0, 0, N);
  }
};
struct heavy_light_decomposition{
	vector<int> p, sz, in, next;
  segment_tree ST;
  int K;
  heavy_light_decomposition(vector<int> &p, vector<vector<int>> &c, vector<long long> &A, int K): p(p), K(K){
    int N = p.size();
    sz = vector<int>(N, 1);
    dfs1(c);
    in = vector<int>(N);
    next = vector<int>(N);
    next[0] = 0;
    int t = 0;
    dfs2(c, t);
    vector<long long> A2(N);
    for (int i = 0; i < N; i++){
      A2[in[i]] = A[i];
    }
    ST = segment_tree(A2, K);
  }
  void dfs1(vector<vector<int>> &c, int v = 0){
    for (int &w : c[v]){
      dfs1(c, w);
      sz[v] += sz[w];
      if (sz[w] > sz[c[v][0]]){
        swap(w, c[v][0]);
      }
    }
  }
  void dfs2(vector<vector<int>> &c, int &t, int v = 0){
    in[v] = t;
    t++;
    for (int w : c[v]){
      if (w == c[v][0]){
        next[w] = next[v];
      } else {
        next[w] = w;
      }
      dfs2(c, t, w);
    }
  }
  void update(int v, int x){
    ST.update(in[v], x);
  }
  int lca(int u, int v){
    while (true){
      if (in[u] > in[v]){
        swap(u, v);
      }
      if (next[u] == next[v]){
        return u;
      }
      v = p[next[v]];
    }
  }
  monoid path_fold(int u, int v){
    int w = lca(u, v);
    monoid L;
    while (next[u] != next[w]){
      L = f(L, ST.range_fold(in[next[u]], in[u] + 1, 1), K);
      u = p[next[u]];
    }
    L = f(L, ST.range_fold(in[w], in[u] + 1, 1), K);
    monoid R;
    while (next[v] != next[w]){
      R = f(ST.range_fold(in[next[v]], in[v] + 1, 0), R, K);
      v = p[next[v]];
    }
    R = f(ST.range_fold(in[w] + 1, in[v] + 1, 0), R, K);
    return f(L, R, K);
  }
};
int main(){
  int N, K;
  cin >> N >> K;
  vector<vector<pair<int, int>>> E(N);
  for (int i = 0; i < N - 1; i++){
    int a, b, c;
    cin >> a >> b >> c;
    E[a].push_back(make_pair(c, b));
    E[b].push_back(make_pair(c, a));
  }
  vector<int> p(N, -1);
  vector<vector<int>> c(N);
  vector<long long> a(N);
  a[0] = 0;
  queue<int> q;
  q.push(0);
  while (!q.empty()){
    int v = q.front();
    q.pop();
    for (auto P : E[v]){
      int w = P.second;
      if (w != p[v]){
        p[w] = v;
        c[v].push_back(w);
        a[w] = P.first - a[v];
        q.push(w);
      }
    }
  }
  heavy_light_decomposition T(p, c, a, K);	
  int Q;
  cin >> Q;
  for (int i = 0; i < Q; i++){
    string S;
    cin >> S;
    if (S == "add"){
      int x, d;
      cin >> x >> d;
      T.update(x, d);
    }
    if (S == "send"){
      int s, t;
      cin >> s >> t;
      cout << T.path_fold(s, t).S << endl;
    }
  }
}
accuracy: 1
time_ms: 430
memory_kb: 41656
final_rank: 13

code_snippet: #include <bits/stdc++.h>
 
using namespace std;
 
template< typename Monoid, typename F >
struct SegmentTree {
    int n, sz;
    vector< Monoid > seg;
 
    const F f;
    const Monoid M1;
     
    SegmentTree(int n, const F f, const Monoid &M1) : n(n), f(f), M1(M1) {
        sz = 1;
        while(sz < n) sz <<= 1;
        seg.assign(2 * sz, M1);
    }
 
    void set(int k, const Monoid &x) {
        seg[k + sz] = x;
    }
 
    void build() {
        for(int k = sz - 1; k > 0; k--) {
            seg[k] = f(seg[2 * k + 0], seg[2 * k + 1]);
        }
    }
 
    void update(int k, const Monoid &x) {
        k += sz;
        seg[k] = x;
        while(k >>= 1) {
            seg[k] = f(seg[2 * k + 0], seg[2 * k + 1]);
        }
    }
 
    Monoid query(int a, int b) {
        Monoid L = M1, R = M1;
        for(a += sz, b += sz; a < b; a >>= 1, b >>= 1) {
            if(a & 1) L = f(L, seg[a++]);
            if(b & 1) R = f(seg[--b], R);
        }
        return f(L, R);
    }
 
    Monoid operator[](const int &k) const {
        return seg[k + sz];
    }
 
    template< typename C >
    int find_subtree(int a, const C &check, Monoid &M, bool type) {
        while(a < sz) {
            Monoid nxt = type ? f(seg[2 * a + type], M) : f(M, seg[2 * a + type]);
            if(check(nxt)) a = 2 * a + type;
            else M = nxt, a = 2 * a + 1 - type;
        }
        return a - sz;
    }
 
    template< typename C >
    int find_first(int a, const C &check) {
        Monoid L = M1;
        if(a <= 0) {
            if(check(f(L, seg[1]))) return find_subtree(1, check, L, false);
            return n;
        }
        int b = sz;
        for(a += sz, b += sz; a < b; a >>= 1, b >>= 1) {
            if(a & 1) {
                Monoid nxt = f(L, seg[a]);
                if(check(nxt)) return find_subtree(a, check, L, false);
                L = nxt;
                ++a;
            }
        }
        return n;
    }
 
    template< typename C >
    int find_last(int b, const C &check) {
        Monoid R = M1;
        if(b >= sz) {
            if(check(f(seg[1], R))) return find_subtree(1, check, R, true);
            return -1;
        }
        int a = sz;
        for(b += sz; a < b; a >>= 1, b >>= 1) {
            if(b & 1) {
                Monoid nxt = f(seg[--b], R);
                if(check(nxt)) return find_subtree(b, check, R, true);
                R = nxt;
            }
        }
        return -1;
    }
};
 
template< typename Monoid, typename F >
SegmentTree< Monoid, F > get_segment_tree(int N, const F& f, const Monoid& M1) {
    return {N, f, M1};
}
 
struct HeavyLightDecomposition {
public:
    vector<vector<int> > g;
    vector<int> sz, in, out, head, rev, par, dep;
 
    HeavyLightDecomposition(const vector<vector<int> > &G) : g(G) {
        sz.resize(g.size(), 0);
        in.resize(g.size(), 0);
        out.resize(g.size(), 0);
        head.resize(g.size(), 0);
        rev.resize(g.size(), 0);
        par.resize(g.size(), 0);
        dep.resize(g.size(), 0);
        dfs_sz(0, -1, 0);
        dfs_hld(0, -1);
    }
 
    // 頂点vからk個遡った頂点
    int la(int v, int k) {
        while (true) {
            int u = head[v];
            if (in[v] - k >= in[u]) return (rev[in[v] - k]);
            k -= in[v] - in[u] + 1;
            v = par[u];
        }
    }
 
    int lca(int u, int v) const {
        for (;; v = par[head[v]]) {
            if (in[u] > in[v]) swap(u, v);
            if (head[u] == head[v]) return (u);
        }
    }
 
    int dist(int u, int v) const {
        return (dep[u] + dep[v] - 2 * dep[lca(u, v)]);
    }
 
    template<typename E, typename F, typename G, typename S>
    E query(int u, int v, const E &ti, const F &f, const G &g, const S &s, bool edge = false) {
        E l = ti, r = ti;
        for (;; v = par[head[v]]) {
            if (in[u] > in[v]) swap(u, v), swap(l, r);
            if (head[u] == head[v]) break;
            l = g(f(in[head[v]], in[v] + 1), l);
        }
        return (s(g(f(in[u] + edge, in[v] + 1), l), r));
    }
 
    template<typename E, typename F, typename G>
    E query(int u, int v, const E &ti, const F &f, const G &g, bool edge = false) {
        return (query(u, v, ti, f, g, g, edge));
    }
 
    template<typename G>
    void update(int u, int v, const G &g, bool edge = false) {
        for (;; v = par[head[v]]) {
            if (in[u] > in[v]) swap(u, v);
            if (head[u] == head[v]) break;
            g(in[head[v]], in[v] + 1);
        }
        g(in[u] + edge, in[v] + 1);
    }
 
    /* {parent, child} */
    vector<pair<int, int> > compress(vector<int> &remark) {
        auto comp = [&](int a, int b) { return (in[a] < in[b]); };
        sort(begin(remark), end(remark), comp);
        remark.erase(unique(begin(remark), end(remark)), end(remark));
        int K = (int) remark.size();
        for (int k = 1; k < K; ++k) remark.emplace_back(lca(remark[k - 1], remark[k]));
        sort(begin(remark), end(remark), comp);
        remark.erase(unique(begin(remark), end(remark)), end(remark));
        vector<pair<int, int> > es;
        vector<int> st;
        for (auto &k : remark) {
            while (!st.empty() && out[st.back()] <= in[k]) st.pop_back();
            if (!st.empty()) es.emplace_back(st.back(), k);
            st.emplace_back(k);
        }
        return (es);
    }
 
private:
    void dfs_sz(int cur, int p, int d) {
        dep[cur] = d;
        par[cur] = p;
        sz[cur] = 1;
        if (!g[cur].empty() && g[cur][0] == p) swap(g[cur][0], g[cur].back());
        for (auto &to : g[cur]) {
            if (to == p) continue;
            dfs_sz(to, cur, d + 1);
            sz[cur] += sz[to];
            if (sz[g[cur][0]] < sz[to]) swap(g[cur][0], to);
        }
    }
 
    void dfs_hld(int cur, int p) {
        static int times = 0;
        in[cur] = times++;
        rev[in[cur]] = cur;
        for (auto &to : g[cur]) {
            if (to == p) continue;
            head[to] = (g[cur][0] == to ? head[cur] : to);
            dfs_hld(to, cur);
        }
        out[cur] = times;
    }
};
 
struct Data {
    long long par_cost;
    long long left_add;
    long long right_add;
    long long cost;
};
 
 
int main(){
    cin.tie(nullptr);
    ios_base::sync_with_stdio(false);
 
    int n, k;
    cin >> n >> k;
 
    vector<vector<int> > graph(n);
    vector<tuple<int, int, int> > edges(n - 1);
    map<pair<int, int>, int> def_cost;
 
    for (int i = 0; i < edges.size(); ++i) {
        int a, b, c;
        cin >> a >> b >> c;
        edges[i] = make_tuple(a, b, c);
        graph[a].emplace_back(b);
        graph[b].emplace_back(a);
        def_cost[minmax(a, b)] = c;
    }
 
    HeavyLightDecomposition hld(graph);
    auto f = [&k](Data a, Data b) {
        if (a.par_cost == -1) {
            return (Data{0, 0, b.right_add, a.cost + b.cost});
        } else if (b.par_cost == -1) {
            return (Data{a.par_cost, a.left_add, 0, a.cost + b.cost});
        }
        long long c = b.par_cost + a.right_add + b.left_add;
        return (Data{a.par_cost, a.left_add, b.right_add, a.cost + b.cost + (c % k == 0 ? 0 : c)});
    };
    auto seg = get_segment_tree(n, f, Data{-1, 0, 0, 0});
    for (int i = 0; i < n; ++i) {
        seg.set(hld.in[i], Data{def_cost[minmax(hld.la(i, 1), i)], 0, 0, 0});
    }
 
    seg.build();
 
    int q;
    cin >> q;
 
    for (int i = 0; i < q; ++i) {
        string com;
        cin >> com;
        if (com == "add") {
            int x, d;
            cin >> x >> d;
            auto data = seg[hld.in[x]];
            data.left_add += d;
            data.right_add += d;
            seg.update(hld.in[x], data);
        } else {
            int s, t;
            cin >> s >> t;
 
            auto query = [&](int u, int v) {
                long long sum = 0;
                while (true) {
                    if (hld.in[u] > hld.in[v]) swap(u, v);
                    if (hld.head[u] == hld.head[v]) break;
                    int head = hld.head[v];
                    int par = hld.par[head];
                    auto head_data = seg[hld.in[head]];
                    auto par_data = seg[hld.in[par]];
                    long long cost = head_data.par_cost + head_data.left_add + par_data.right_add;
                    sum += seg.query(hld.in[head], hld.in[v] + 1).cost + (cost % k == 0 ? 0 : cost);
                    v = par;
                }
                return (seg.query(hld.in[u], hld.in[v] + 1).cost + sum);
            };
 
            cout << query(s, t) << '\n';
        }
    }
 
    return (0);
}
accuracy: 1
time_ms: 120
memory_kb: 39916
final_rank: 6