#include #ifdef LOCAL #include #else #define debug(...) void(0) #endif #ifdef _MSC_VER #include #endif namespace atcoder { namespace internal { // @param n `0 <= n` // @return minimum non-negative `x` s.t. `n <= 2**x` int ceil_pow2(int n) { int x = 0; while ((1U << x) < (unsigned int)(n)) x++; return x; } // @param n `1 <= n` // @return minimum non-negative `x` s.t. `(n & (1 << x)) != 0` constexpr int bsf_constexpr(unsigned int n) { int x = 0; while (!(n & (1 << x))) x++; return x; } // @param n `1 <= n` // @return minimum non-negative `x` s.t. `(n & (1 << x)) != 0` int bsf(unsigned int n) { #ifdef _MSC_VER unsigned long index; _BitScanForward(&index, n); return index; #else return __builtin_ctz(n); #endif } } // namespace internal } // namespace atcoder namespace atcoder { template struct lazy_segtree { public: lazy_segtree() : lazy_segtree(0) {} explicit lazy_segtree(int n) : lazy_segtree(std::vector(n, e())) {} explicit lazy_segtree(const std::vector& v) : _n(int(v.size())) { log = internal::ceil_pow2(_n); size = 1 << log; d = std::vector(2 * size, e()); lz = std::vector(size, id()); for (int i = 0; i < _n; i++) d[size + i] = v[i]; for (int i = size - 1; i >= 1; i--) { update(i); } } void set(int p, S x) { assert(0 <= p && p < _n); p += size; for (int i = log; i >= 1; i--) push(p >> i); d[p] = x; for (int i = 1; i <= log; i++) update(p >> i); } S get(int p) { assert(0 <= p && p < _n); p += size; for (int i = log; i >= 1; i--) push(p >> i); return d[p]; } S prod(int l, int r) { assert(0 <= l && l <= r && r <= _n); if (l == r) return e(); l += size; r += size; for (int i = log; i >= 1; i--) { if (((l >> i) << i) != l) push(l >> i); if (((r >> i) << i) != r) push((r - 1) >> i); } S sml = e(), smr = e(); while (l < r) { if (l & 1) sml = op(sml, d[l++]); if (r & 1) smr = op(d[--r], smr); l >>= 1; r >>= 1; } return op(sml, smr); } S all_prod() { return d[1]; } void apply(int p, F f) { assert(0 <= p && p < _n); p += size; for (int i = log; i >= 1; i--) push(p >> i); d[p] = mapping(f, d[p]); for (int i = 1; i <= log; i++) update(p >> i); } void apply(int l, int r, F f) { assert(0 <= l && l <= r && r <= _n); if (l == r) return; l += size; r += size; for (int i = log; i >= 1; i--) { if (((l >> i) << i) != l) push(l >> i); if (((r >> i) << i) != r) push((r - 1) >> i); } { int l2 = l, r2 = r; while (l < r) { if (l & 1) all_apply(l++, f); if (r & 1) all_apply(--r, f); l >>= 1; r >>= 1; } l = l2; r = r2; } for (int i = 1; i <= log; i++) { if (((l >> i) << i) != l) update(l >> i); if (((r >> i) << i) != r) update((r - 1) >> i); } } template int max_right(int l) { return max_right(l, [](S x) { return g(x); }); } template int max_right(int l, G g) { assert(0 <= l && l <= _n); assert(g(e())); if (l == _n) return _n; l += size; for (int i = log; i >= 1; i--) push(l >> i); S sm = e(); do { while (l % 2 == 0) l >>= 1; if (!g(op(sm, d[l]))) { while (l < size) { push(l); l = (2 * l); if (g(op(sm, d[l]))) { sm = op(sm, d[l]); l++; } } return l - size; } sm = op(sm, d[l]); l++; } while ((l & -l) != l); return _n; } template int min_left(int r) { return min_left(r, [](S x) { return g(x); }); } template int min_left(int r, G g) { assert(0 <= r && r <= _n); assert(g(e())); if (r == 0) return 0; r += size; for (int i = log; i >= 1; i--) push((r - 1) >> i); S sm = e(); do { r--; while (r > 1 && (r % 2)) r >>= 1; if (!g(op(d[r], sm))) { while (r < size) { push(r); r = (2 * r + 1); if (g(op(d[r], sm))) { sm = op(d[r], sm); r--; } } return r + 1 - size; } sm = op(d[r], sm); } while ((r & -r) != r); return 0; } private: int _n, size, log; std::vector d; std::vector lz; void update(int k) { d[k] = op(d[2 * k], d[2 * k + 1]); } void all_apply(int k, F f) { d[k] = mapping(f, d[k]); if (k < size) lz[k] = composition(f, lz[k]); } void push(int k) { all_apply(2 * k, lz[k]); all_apply(2 * k + 1, lz[k]); lz[k] = id(); } }; } // namespace atcoder struct HeavyLightDecomposition { std::vector> G; // child of vertex v on heavy edge is G[v].front() if it is not parent of v int n, time; std::vector par, // parent of vertex v sub, // size of subtree whose root is v dep, // distance bitween root and vertex v head, // vertex that is the nearest to root on heavy path of vertex v tree_id, // id of tree vertex v belongs to vertex_id, // id of vertex v (consecutive on heavy paths) vertex_id_inv; // vertex_id_inv[vertex_id[v]] = v HeavyLightDecomposition(int n) : G(n), n(n), time(0), par(n, -1), sub(n), dep(n, 0), head(n), tree_id(n, -1), vertex_id(n, -1), vertex_id_inv(n) {} void add_edge(int u, int v) { assert(0 <= u and u < n); assert(0 <= v and v < n); G[u].emplace_back(v); G[v].emplace_back(u); } void build(std::vector roots = {0}) { int tree_id_cur = 0; for (int& r : roots) { assert(0 <= r and r < n); dfs_sz(r); head[r] = r; dfs_hld(r, tree_id_cur++); } assert(time == n); for (int v = 0; v < n; v++) vertex_id_inv[vertex_id[v]] = v; } int idx(int v) const { return vertex_id[v]; } int la(int v, int k) const { assert(0 <= v and v < n); assert(0 <= k and k <= dep[v]); while (1) { int u = head[v]; if (vertex_id[v] - k >= vertex_id[u]) return vertex_id_inv[vertex_id[v] - k]; k -= vertex_id[v] - vertex_id[u] + 1; v = par[u]; } } int lca(int u, int v) const { assert(0 <= u and u < n); assert(0 <= v and v < n); assert(tree_id[u] == tree_id[v]); for (;; v = par[head[v]]) { if (vertex_id[u] > vertex_id[v]) std::swap(u, v); if (head[u] == head[v]) return u; } } int jump(int s, int t, int i) const { assert(0 <= s and s < n); assert(0 <= t and t < n); assert(0 <= i); if (tree_id[s] != tree_id[t]) return -1; if (i == 0) return s; int p = lca(s, t), d = dep[s] + dep[t] - 2 * dep[p]; if (d < i) return -1; if (dep[s] - dep[p] >= i) return la(s, i); return la(t, d - i); } int distance(int u, int v) const { assert(0 <= u and u < n); assert(0 <= v and v < n); assert(tree_id[u] == tree_id[v]); return dep[u] + dep[v] - 2 * dep[lca(u, v)]; } template void query_path(int u, int v, const F& f, bool vertex = false) const { assert(0 <= u and u < n); assert(0 <= v and v < n); assert(tree_id[u] == tree_id[v]); int p = lca(u, v); for (auto& e : ascend(u, p)) f(e.second, e.first + 1); if (vertex) f(vertex_id[p], vertex_id[p] + 1); for (auto& e : descend(p, v)) f(e.first, e.second + 1); } template void query_path_noncommutative(int u, int v, const F& f, bool vertex = false) const { assert(0 <= u and u < n); assert(0 <= v and v < n); assert(tree_id[u] == tree_id[v]); int p = lca(u, v); for (auto& e : ascend(u, p)) f(e.first + 1, e.second); if (vertex) f(vertex_id[p], vertex_id[p] + 1); for (auto& e : descend(p, v)) f(e.first, e.second + 1); } template void query_subtree(int u, const F& f, bool vertex = false) const { assert(0 <= u and u < n); f(vertex_id[u] + !vertex, vertex_id[u] + sub[u]); } private: void dfs_sz(int v) { sub[v] = 1; if (!G[v].empty() and G[v].front() == par[v]) std::swap(G[v].front(), G[v].back()); for (int& u : G[v]) { if (u == par[v]) continue; par[u] = v; dep[u] = dep[v] + 1; dfs_sz(u); sub[v] += sub[u]; if (sub[u] > sub[G[v].front()]) std::swap(u, G[v].front()); } } void dfs_hld(int v, int tree_id_cur) { vertex_id[v] = time++; tree_id[v] = tree_id_cur; for (int& u : G[v]) { if (u == par[v]) continue; head[u] = (u == G[v][0] ? head[v] : u); dfs_hld(u, tree_id_cur); } } std::vector> ascend(int u, int v) const { // [u, v), v is ancestor of u std::vector> res; while (head[u] != head[v]) { res.emplace_back(vertex_id[u], vertex_id[head[u]]); u = par[head[u]]; } if (u != v) res.emplace_back(vertex_id[u], vertex_id[v] + 1); return res; } std::vector> descend(int u, int v) const { // (u, v], u is ancestor of v if (u == v) return {}; if (head[u] == head[v]) return {{vertex_id[u] + 1, vertex_id[v]}}; auto res = descend(u, par[head[v]]); res.emplace_back(vertex_id[head[v]], vertex_id[v]); return res; } }; using namespace std; typedef long long ll; #define all(x) begin(x), end(x) constexpr int INF = (1 << 30) - 1; constexpr long long IINF = (1LL << 60) - 1; constexpr int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1}; template istream& operator>>(istream& is, vector& v) { for (auto& x : v) is >> x; return is; } template ostream& operator<<(ostream& os, const vector& v) { auto sep = ""; for (const auto& x : v) os << exchange(sep, " ") << x; return os; } template bool chmin(T& x, U&& y) { return y < x and (x = forward(y), true); } template bool chmax(T& x, U&& y) { return x < y and (x = forward(y), true); } template void mkuni(vector& v) { sort(begin(v), end(v)); v.erase(unique(begin(v), end(v)), end(v)); } template int lwb(const vector& v, const T& x) { return lower_bound(begin(v), end(v), x) - begin(v); } ll op(ll l, ll r) { return min(l, r); } ll e() { return IINF; } ll mapping(ll l, ll r) { return l + r; } ll composition(ll l, ll r) { return l + r; } ll id() { return 0; } bool f(ll x) { return x > 0; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int N, Q; cin >> N >> Q; vector a(N - 1), b(N - 1); vector c(N - 1); for (int i = 0; i < N - 1; i++) cin >> a[i] >> b[i] >> c[i], a[i]--, b[i]--; HeavyLightDecomposition HLD(N); for (int i = 0; i < N - 1; i++) HLD.add_edge(a[i], b[i]); HLD.build(); vector init(N, e()); vector ch(N), par(N, -1); vector> G(N); for (int i = 0; i < N - 1; i++) { G[a[i]].emplace_back(i); G[b[i]].emplace_back(i); if (HLD.dep[a[i]] < HLD.dep[b[i]]) swap(a[i], b[i]); par[a[i]] = i; init[HLD.idx(a[i])] = c[i]; ch[HLD.idx(a[i])] = a[i]; } atcoder::lazy_segtree seg(init); int ans = N; vector sum(N, 0); vector alive(N - 1, true); auto kill = [&](int idx) { alive[idx] = false; seg.set(HLD.idx(a[idx]), IINF); ans--; auto q = [&](int l, int r) { seg.apply(l, r, sum[a[idx]]); }; HLD.query_path(0, a[idx], q); }; auto dfs = [&](auto self, int v) -> void { assert(v != 0); for (int& idx : G[v]) { int u = a[idx] ^ b[idx] ^ v; if (not alive[idx]) continue; kill(idx); self(self, u); } }; auto query = [&](int v, int x) { sum[v] += x; auto q = [&](int l, int r) { seg.apply(l, r, -x); }; HLD.query_path(0, v, q); int e = seg.min_left(N) - 1; if (e == -1) return; int idx = par[ch[e]]; kill(idx); dfs(dfs, a[idx]); }; for (; Q--;) { int type; cin >> type; if (type == 1) { int v, x; cin >> v >> x; query(--v, x); } else { cout << ans << '\n'; } } return 0; }