#include <iostream>
#include <vector>
#include <tuple>
#include <algorithm>
#include <cassert>
const int N = 120005;
const int C = 1000000;
inline int read_int()
{
int x = 0;
char c;
do {
c = getchar_unlocked();
} while (c < '0');
do {
x = (x << 1) + (x << 3) + (c - '0');
c = getchar_unlocked();
} while (c >= '0');
return x;
}
struct EdgeInterval
{
int edge;
int s, e;
EdgeInterval() = default;
EdgeInterval(const int _edge, const int _s, const int _e) : edge(_edge), s(_s), e(_e) {
}
};
std::vector<EdgeInterval> intvs[C];
std::vector<std::pair<int, int>> edge_queries[C]; // (time, edge)
std::tuple<int, int, int> queries[N];
std::pair<int, int> tree_edge[N - 1];
std::pair<int, int> last_update[N - 1]; // (time, color)
long long query_answers[C];
std::vector<int> color_updates[N + 1];
long long sum_tree[2 * C];
int idx[C];
// for solve_color
std::vector<int> tree[4 * 4 * N];
std::vector<std::pair<int, int>> normalized_queries[4 * N];
std::vector<int> deltas[C];
long long *fvalue_at[C];
long long f(int x)
{
return static_cast<long long>(x) * (x - 1) / 2;
}
struct DisjointSets
{
int parent[N];
int weight[N];
std::vector<std::pair<int, int>> stk;
long long fvalue;
DisjointSets()
{
for (int i = 0; i < N; ++ i) {
parent[i] = i;
weight[i] = 1;
}
fvalue = 0;
}
int find_root(int x) const {
return (parent[x] == x) ? x : find_root(parent[x]);
}
void unite(int x, int y)
{
x = find_root(x);
y = find_root(y);
if (x == y) {
return;
}
if (y > x) {
std::swap(x, y);
}
stk.emplace_back(x, y);
fvalue -= f(weight[x]);
fvalue -= f(weight[y]);
weight[x] += weight[y];
parent[y] = x;
fvalue += f(weight[x]);
}
int get_size(int node) const
{
return weight[find_root(node)];
}
int get_snapshot_idx() const
{
return static_cast<int>(stk.size());
}
void undo_at(const int snapshot)
{
while (static_cast<int>(stk.size()) > snapshot) {
int x, y;
x = stk.back().first;
y = stk.back().second;
fvalue -= f(weight[x]);
parent[y] = y;
weight[x] -= weight[y];
fvalue += f(weight[x]);
fvalue += f(weight[y]);
stk.pop_back();
}
}
} DS;
void update_tree(int node, int l, int r, int ql, int qr, int edgeidx)
{
if (ql <= l && r <= qr) {
tree[node].push_back(edgeidx);
} else {
int mid = (l + r) >> 1;
if (ql <= mid) {
update_tree(2 * node + 1, l, mid, ql, qr, edgeidx);
}
if (mid < qr) {
update_tree(2 * node + 2, mid + 1, r, ql, qr, edgeidx);
}
}
}
void dfs(const int color, int node, int l, int r)
{
const int snapshot = DS.get_snapshot_idx();
for (auto&& edgeidx : tree[node]) {
DS.unite(tree_edge[edgeidx].first, tree_edge[edgeidx].second);
}
tree[node].clear();
if (l == r) {
fvalue_at[color][l] = DS.fvalue;
for (auto&& edge_query : normalized_queries[l]) {
query_answers[edge_query.first] = f(DS.get_size(tree_edge[edge_query.second].first));
}
normalized_queries[l].clear();
} else {
int mid = (l + r) >> 1;
dfs(color, 2 * node + 1, l, mid);
dfs(color, 2 * node + 2, mid + 1, r);
}
DS.undo_at(snapshot);
}
void solve_color(int color)
{
for (auto&& x : intvs[color]) {
deltas[color].push_back(x.s);
deltas[color].push_back(x.e);
deltas[color].push_back(x.e + 1);
}
for (auto&& x : edge_queries[color]) {
deltas[color].push_back(x.first);
}
std::sort(deltas[color].begin(), deltas[color].end());
deltas[color].erase(std::unique(deltas[color].begin(), deltas[color].end()), deltas[color].end());
int n = static_cast<int>(deltas[color].size());
fvalue_at[color] = new long long[n];
for (auto&& x : intvs[color]) {
int a = std::lower_bound(deltas[color].begin(), deltas[color].end(), x.s) - deltas[color].begin();
int b = std::lower_bound(deltas[color].begin(), deltas[color].end(), x.e) - deltas[color].begin();
update_tree(0, 0, n - 1, a, b, x.edge);
}
for (auto&& x : edge_queries[color]) {
normalized_queries[std::lower_bound(deltas[color].begin(), deltas[color].end(), x.first) - deltas[color].begin()].push_back(x);
}
dfs(color, 0, 0, n - 1);
}
int main()
{
int n = read_int();
for (int i = 0; i < n - 1; ++ i) {
int x = read_int() - 1, y = read_int() - 1, color = read_int() - 1;
tree_edge[i] = std::make_pair(x, y);
last_update[i] = std::make_pair(-1, color);
}
int q = read_int();
for (int i = 0; i < q; ++ i) {
int qtype = read_int();
if (qtype == 1) {
int edgeidx = read_int() - 1, color = read_int() - 1;
if (last_update[edgeidx].second != color) {
intvs[last_update[edgeidx].second].emplace_back(edgeidx, last_update[edgeidx].first, i - 1);
last_update[edgeidx] = std::make_pair(i, color);
}
queries[i] = std::make_tuple(-1, 0, 0);
} else {
if (qtype == 2) {
int x = read_int() - 1, y = read_int() - 1;
queries[i] = std::make_tuple(0, x, y);
} else {
int edgeidx = read_int() - 1;
queries[i] = std::make_tuple(1, edgeidx, -1);
edge_queries[last_update[edgeidx].second].emplace_back(i, edgeidx);
}
}
}
for (int i = 0; i < n - 1; ++ i) {
intvs[last_update[i].second].emplace_back(i, last_update[i].first, q);
}
for (int i = 0; i < q; ++ i) {
query_answers[i] = -1;
}
for (int i = 0; i < C; ++ i) {
if (!intvs[i].empty()) {
//deltas[i].push_back(q);
solve_color(i);
for (auto&& x : deltas[i]) {
if (x != -1) {
color_updates[x].push_back(i);
} else {
sum_tree[i + C] = fvalue_at[i][0];
idx[i] = 1;
}
}
}
}
for (int i = C - 1; i > 0; -- i) {
sum_tree[i] = sum_tree[i << 1] + sum_tree[i << 1 | 1];
}
for (int i = 0; i < q; ++ i) {
for (auto&& color : color_updates[i]) {
sum_tree[color + C] = fvalue_at[color][idx[color]];
for (int node = color + C; node > 0; node >>= 1) {
sum_tree[node >> 1] = sum_tree[node ^ 1] + sum_tree[node];
}
idx[color]++;
}
if (std::get<0>(queries[i]) == 0) {
query_answers[i] = 0ULL;
for (int l = C + std::get<1>(queries[i]), r = 1 + C + std::get<2>(queries[i]); l < r; l >>= 1, r >>= 1) {
if (l & 1) {
query_answers[i] += sum_tree[l++];
}
if (r & 1) {
query_answers[i] += sum_tree[--r];
}
}
}
}
for (int i = 0; i < q; ++ i) {
if (query_answers[i] != -1) {
printf("%lld\n", query_answers[i]);
}
}
}