#include <bits/stdc++.h>
using std::cerr;
struct top_tree_node {
mutable top_tree_node* p = nullptr;
top_tree_node* c[3] = {nullptr, nullptr, nullptr};
int d() const {
assert(p);
if (this == p->c[0]) {
return 0;
} else if (this == p->c[1]) {
return 1;
} else if (this == p->c[2]) {
return 2;
} else assert(false);
}
top_tree_node*& p_c() const { return p->c[d()]; } // p->c which points to you
// 3 types of verts: path edges, path verts, non-path edges
bool is_path;
bool is_vert;
bool r() const { return !p || p->is_path != is_path; }
int __id;
bool flip_path = false;
int64_t subtree_size = 0;
int64_t path_size = 0;
int64_t subtree_lazy = 0;
int64_t path_lazy = 0;
int64_t tot_A = 0;
int64_t own_A = 0;
std::array<int64_t, 2> tot_sub_d = {0,0};
std::array<int64_t, 2> tot_ans = {0,0};
void do_flip_path() {
assert(is_path);
flip_path ^= 1;
std::swap(tot_sub_d[0], tot_sub_d[1]);
std::swap(tot_ans[0], tot_ans[1]);
}
void do_subtree_increment(int64_t v = 1) {
//cerr << "doing subtree increment " << __id << ' ' << v << '\n';
subtree_lazy += v;
if (is_vert) own_A += v;
tot_A += subtree_size * v;
tot_ans[0] += tot_sub_d[0] * v;
tot_ans[1] += tot_sub_d[1] * v;
}
void do_path_increment(int64_t v = 1) {
//cerr << "doing path increment " << __id << ' ' << v << '\n';
assert(is_path);
path_lazy += v;
if (is_vert) own_A += v;
tot_A += path_size * v;
tot_ans[0] += (path_size * (path_size-1) / 2) * v;
tot_ans[1] += (path_size * (path_size-1) / 2) * v;
}
void downdate() {
// fortunately, this doesn't interact with our ops
if (flip_path) {
assert(is_path);
if (!is_vert) {
if (c[0]) c[0]->do_flip_path();
if (c[1]) c[1]->do_flip_path();
}
std::swap(c[0], c[1]);
flip_path = false;
}
if (subtree_lazy) {
if (c[0]) c[0]->do_subtree_increment(subtree_lazy);
if (c[1]) c[1]->do_subtree_increment(subtree_lazy);
if (c[2]) c[2]->do_subtree_increment(subtree_lazy);
subtree_lazy = 0;
}
if (path_lazy) {
assert(is_path);
if (!is_vert) {
//std::cerr << "downdating path lazy" << ' ' << __id << ' ' << path_lazy << '\n';
assert(c[0] && c[1] && !c[2]);
c[0]->do_path_increment(path_lazy);
c[1]->do_path_increment(path_lazy);
}
path_lazy = 0;
}
}
void update() {
assert(!flip_path && !subtree_lazy && !path_lazy);
if (is_vert) assert(is_path);
subtree_size = is_vert;
for (int z = 0; z <= 2; z++) {
if (c[z]) subtree_size += c[z]->subtree_size;
}
if (!is_path) {
path_size = 0;
} else {
path_size = is_vert;
if (!is_vert) {
for (int z = 0; z < 2; z++) {
if (c[z]) path_size += c[z]->path_size;
}
}
assert(path_size >= 1);
}
tot_A = is_vert ? own_A : 0;
for (int z = 0; z <= 2; z++) {
if (c[z]) tot_A += c[z]->tot_A;
}
if (is_path && !is_vert) {
assert(c[0] && c[1]);
assert(!c[2]);
for (int z = 0; z < 2; z++) {
tot_sub_d[z] = c[z]->tot_sub_d[z] + c[z]->path_size * c[!z]->subtree_size + c[!z]->tot_sub_d[z];
tot_ans[z] = c[z]->tot_ans[z] + c[z]->path_size * c[!z]->tot_A + c[!z]->tot_ans[z];
}
} else {
tot_sub_d[0] = 0;
tot_ans[0] = 0;
for (int z = 0; z < 2; z++) {
if (c[z]) {
tot_sub_d[0] += c[z]->tot_sub_d[0];
tot_ans[0] += c[z]->tot_ans[0];
}
}
if (c[2]) {
assert(!is_path);
tot_sub_d[0] += c[2]->subtree_size + c[2]->tot_sub_d[0];
tot_ans[0] += c[2]->tot_A + c[2]->tot_ans[0];
}
tot_sub_d[1] = tot_sub_d[0];
tot_ans[1] = tot_ans[0];
}
}
void downdate_all() {
if (p) p->downdate_all();
downdate();
}
void update_all() {
update();
if (p) p->update_all();
}
private:
void rot() {
assert(!is_vert);
assert(!r());
top_tree_node* pa = p;
int x = d(); assert(x == 0 || x == 1);
top_tree_node* ch = c[!x];
if (pa->p) pa->p_c() = this;
this->p = pa->p;
pa->c[x] = ch;
if (ch) ch->p = pa;
this->c[!x] = pa;
pa->p = this;
pa->update();
}
void rot_2(int c_d) {
assert(!is_vert);
assert(!r());
assert(c[c_d]);
assert(!c[c_d]->is_vert);
if (d() == c_d) {
rot();
return;
}
top_tree_node* pa = p;
int x = d(); assert(x == 0 || x == 1);
assert(c_d == !x);
top_tree_node* ch = c[c_d]->c[!x];
if (pa->p) pa->p_c() = this;
this->p = pa->p;
pa->c[x] = ch;
if (ch) ch->p = pa;
this->c[c_d]->c[!x] = pa;
pa->p = this->c[c_d];
pa->update();
}
void splay_dir(int x) {
while (!r() && d() == x) {
if (!p->r() && p->d() == x) {
p->rot();
}
rot();
}
}
void splay_2(int c_d) {
assert(!is_vert && is_path);
assert(c[c_d] && !c[c_d]->is_vert);
while (!r()) {
if (!p->r()) {
if (p->d() == d()) {
p->rot();
} else {
rot_2(c_d);
}
}
rot_2(c_d);
}
}
void splay_2() {
assert(!is_vert && is_path);
assert(!r());
p->splay_2(d());
}
void splay_vert() {
assert(is_vert);
if (r()) {
return;
}
p->splay_dir(d());
if (p->r()) {
return;
}
assert(p->d() != d());
// we have a preference to be the left child
if (d() == 1) {
p->rot();
}
assert(d() == 0);
p->splay_2();
assert(d() == 0);
assert(p->d() == 1);
assert(p->p->r());
}
void splay() {
assert(!is_vert);
while (!r()) {
if (!p->r()) {
if (p->d() == d()) {
p->rot();
} else {
rot();
}
}
rot();
}
}
top_tree_node* cut_right() {
assert(is_vert && is_path);
splay_vert();
if (r() || d() == 1) {
assert(r() || (d() == 1 && p->r()));
assert(c[0] == nullptr);
return nullptr;
}
top_tree_node* pa = p;
assert(pa->r() || (pa->d() == 1 && pa->p->r()));
assert(!pa->is_vert);
assert(pa->is_path);
assert(pa->c[0] == this);
assert(pa->c[2] == nullptr);
if (pa->p) pa->p_c() = this;
this->p = pa->p;
pa->is_path = false;
pa->c[2] = pa->c[1]; // don't need to change the parent
pa->c[0] = c[0]; if (c[0]) c[0]->p = pa;
pa->c[1] = c[1]; if (c[1]) c[1]->p = pa;
c[0] = nullptr;
c[1] = pa; pa->p = this;
assert(c[2] == nullptr);
assert(c[0] == nullptr);
pa->update();
return pa;
}
top_tree_node* splice_non_path() {
assert(!is_path);
assert(!is_vert);
splay();
assert(p && p->is_vert && p->is_path);
p->cut_right();
if (!p->is_path) rot();
assert(p && p->is_vert && p->is_path);
assert(p->r() || (p->d() == 1 && p->p->r()));
assert(p->c[d()] == this && p->c[!d()] == nullptr);
top_tree_node* pa = p;
if (pa->p) pa->p_c() = this;
this->p = pa->p;
pa->c[0] = c[0]; if (c[0]) c[0]->p = pa;
pa->c[1] = c[1]; if (c[1]) c[1]->p = pa;
assert(c[2] && c[2]->is_path);
c[1] = c[2]; // don't need to change parent
c[0] = pa; pa->p = this;
c[2] = nullptr;
is_path = true;
pa->update();
return pa;
}
void splice_all(top_tree_node*& res) {
if (!is_path) {
res = splice_non_path();
}
assert(is_path);
if (!p) return;
p->splice_all(res);
}
public:
top_tree_node* expose() {
assert(is_vert);
downdate_all();
top_tree_node* res = nullptr;
splice_all(res);
cut_right();
update_all();
return res;
}
void meld_path_end() {
assert(!p);
top_tree_node* rt = this;
while (true) {
rt->downdate();
if (rt->is_vert) break;
rt = rt->c[1];
}
assert(rt->is_vert);
rt->splay_vert();
if (rt->c[0] && rt->c[1]) {
top_tree_node* ch = rt->c[1];
while (true) {
ch->downdate();
if (!ch->c[0]) break;
ch = ch->c[0];
}
ch->splay();
assert(ch->c[0] == nullptr);
ch->c[0] = rt->c[0];
ch->c[0]->p = ch;
rt->c[0] = nullptr;
ch->update();
} else if (rt->c[0]) {
rt->c[1] = rt->c[0];
rt->c[0] = nullptr;
}
assert(rt->c[0] == nullptr);
rt->update_all();
}
void make_root() {
expose();
top_tree_node* rt = this;
while (rt->p) {
assert(rt->d() == 1);
rt = rt->p;
}
rt->do_flip_path();
rt->meld_path_end();
expose();
assert(!p);
}
// link v2 as a child of v1 with edge e
friend void link(top_tree_node* e, top_tree_node* v1, top_tree_node* v2) {
assert(e && v1 && v2);
assert(!e->c[0] && !e->c[1] && !e->c[2]);
v1->expose(); while (v1->p) v1 = v1->p;
v2->make_root();
assert(!v1->p);
assert(!v2->p);
e->is_path = true, e->is_vert = false;
e->c[0] = v1;
v1->p = e;
e->c[1] = v2;
v2->p = e;
e->update();
}
friend std::pair<top_tree_node*, top_tree_node*> cut(top_tree_node* e) {
assert(!e->p);
assert(e->is_path);
assert(!e->is_vert);
e->downdate();
top_tree_node* l = e->c[0];
top_tree_node* r = e->c[1];
assert(l && r);
e->c[0] = e->c[1] = nullptr;
l->p = r->p = nullptr;
assert(e->c[2] == nullptr);
l->meld_path_end();
return {l, r};
}
friend top_tree_node* get_path(top_tree_node* a, top_tree_node* b) {
assert(a->is_vert && b->is_vert);
a->make_root();
b->expose();
if (a == b) {
assert(!b->p);
return b;
}
assert(!b->p->p);
return b->p;
}
friend top_tree_node* get_subtree(top_tree_node* rt, top_tree_node* n) {
rt->make_root();
n->expose();
return n;
}
};
int main() {
using namespace std;
ios_base::sync_with_stdio(false), cin.tie(nullptr);
int N; cin >> N;
vector<top_tree_node> nodes(2*N-1);
for (int i = 0; i < 2 * N - 1; i++) {
nodes[i].__id = i;
}
for (int i = 0; i < N; i++) {
top_tree_node* n = &nodes[i];
n->is_path = n->is_vert = true;
n->update();
}
for (int e = 0; e < N-1; e++) {
int u, v; cin >> u >> v; u--, v--;
top_tree_node* a = &nodes[u];
top_tree_node* b = &nodes[v];
link(&nodes[N+e], a, b);
}
int Q; cin >> Q;
while (Q--) {
int op; cin >> op;
if (op == 1) {
int u, v; cin >> u >> v; u--, v--;
auto sub = get_subtree(&nodes[u], &nodes[v]);
sub->do_subtree_increment();
sub->downdate();
sub->update_all();
} else if (op == 2) {
int u, v; cin >> u >> v; u--, v--;
auto pth = get_path(&nodes[u], &nodes[v]);
pth->do_path_increment();
pth->downdate();
pth->update_all();
} else if (op == 3) {
int v; cin >> v; v--;
nodes[v].make_root();
cout << nodes[v].tot_ans[0] << '\n';
} else assert(false);
/*
cerr << "dumping tree" << '\n';
for (int z = 0; z < 2*N-1; z++) {
cerr << "node " << z << '\n';
cerr << "par: " << (nodes[z].p ? nodes[z].p->__id : -1) << '\n';
cerr << "subtree_size: " << nodes[z].subtree_size << '\n';
cerr << "path_size: " << nodes[z].path_size << '\n';
cerr << "subtree_lazy: " << nodes[z].subtree_lazy << '\n';
cerr << "path_lazy: " << nodes[z].path_lazy << '\n';
cerr << "tot_A: " << nodes[z].tot_A << '\n';
cerr << "own_A: " << nodes[z].own_A << '\n';
cerr << "tot_sub_d[0]: " << nodes[z].tot_sub_d[0] << '\n';
cerr << "tot_sub_d[1]: " << nodes[z].tot_sub_d[1] << '\n';
cerr << "tot_ans[0]: " << nodes[z].tot_ans[0] << '\n';
cerr << "tot_ans[1]: " << nodes[z].tot_ans[1] << '\n';
}
cerr << '\n';
*/
}
return 0;
}