Qtree1
将边权变为这条边连接的两个点中深度更深的点的点权,这样就可以变为带修改链上最大点权。直接树链剖分即可。
下面是一份C语言代码
#include
#include
#define MAXN 10001
inline int read(){
int a = 0;
int f = 0;
char c = getchar();
while(!isdigit(c)){
if(c == '-')
f = 1;
c = getchar();
}
while(isdigit(c)){
a = (a << 3) + (a << 1) + (c ^ '0');
c = getchar();
}
return f ? -a : a;
}
char output[20];
inline void print(int x){
int dirN = 18;
if(x == 0)
fwrite("0" , sizeof(char) , 1 , stdout);
else{
if(x < 0){
x = -x;
fwrite("-" , sizeof(char) , 1 , stdout);
}
while(x){
output[--dirN] = x % 10 + 48;
x /= 10;
}
fwrite(output + dirN , 1 , strlen(output + dirN) , stdout);
}
fwrite("\n" , 1 , 1 , stdout);
}
inline int max(int a , int b){
return a > b ? a : b;
}
struct node{
int l , r , maxN;
}Tree[MAXN << 2];
struct Edge{
int end , upEd , w;
}Ed[MAXN << 1];
int head[MAXN] , start[MAXN << 1] , size[MAXN] , son[MAXN] , fa[MAXN] , dep[MAXN];
int top[MAXN] , ind[MAXN] , rk[MAXN] , val[MAXN] , N , cntEd , ts;
inline void addEd(int a , int b , int c){
Ed[++cntEd].end = b;
Ed[cntEd].upEd = head[a];
Ed[cntEd].w = c;
head[a] = cntEd;
}
void dfs1(int dir , int father , int w){
val[dir] = w;
dep[dir] = dep[fa[dir] = father] + 1;
size[dir] = 1;
int i;
for(i = head[dir] ; i ; i = Ed[i].upEd)
if(!dep[Ed[i].end]){
dfs1(Ed[i].end , dir , Ed[i].w);
size[dir] += size[Ed[i].end];
if(size[son[dir]] < size[Ed[i].end])
son[dir] = Ed[i].end;
}
}
void dfs2(int dir , int t){
top[dir] = t;
rk[ind[dir] = ++ts] = dir;
if(!son[dir])
return;
dfs2(son[dir] , t);
int i;
for(i = head[dir] ; i ; i = Ed[i].upEd)
if(Ed[i].end != fa[dir] && Ed[i].end != son[dir])
dfs2(Ed[i].end , Ed[i].end);
}
inline void pushup(int dir){
Tree[dir].maxN = max(Tree[dir << 1].maxN , Tree[dir << 1 | 1].maxN);
}
void init(int dir , int l , int r){
Tree[dir].l = l;
Tree[dir].r = r;
if(l == r)
Tree[dir].maxN = val[rk[l]];
else{
init(dir << 1 , l , l + r >> 1);
init(dir << 1 | 1 , (l + r >> 1) + 1 , r);
pushup(dir);
}
}
void change(int dir , int tar , int val){
if(Tree[dir].l == Tree[dir].r){
Tree[dir].maxN = val;
return;
}
if(Tree[dir].l + Tree[dir].r >> 1 >= tar)
change(dir << 1 , tar , val);
else
change(dir << 1 | 1 , tar , val);
pushup(dir);
}
int findMax(int dir , int l , int r){
if(Tree[dir].l >= l && Tree[dir].r <= r)
return Tree[dir].maxN;
int maxN = 0;
if(l <= Tree[dir].l + Tree[dir].r >> 1)
maxN = max(maxN , findMax(dir << 1 , l , r));
if(r > Tree[dir].l + Tree[dir].r >> 1)
maxN = max(maxN , findMax(dir << 1 | 1 , l , r));
return maxN;
}
inline void work(int x , int y){
if(x == y){
print(0);
return;
}
int tx = top[x] , ty = top[y] , maxN = -0x7fffffff;
while(tx != ty)
if(dep[tx] >= dep[ty]){
maxN = max(maxN , findMax(1 , ind[tx] , ind[x]));
x = fa[tx];
tx = top[x];
}
else{
maxN = max(maxN , findMax(1 , ind[ty] , ind[y]));
y = fa[ty];
ty = top[y];
}
if(ind[x] < ind[y])
maxN = max(maxN , findMax(1 , ind[x] + 1 , ind[y]));
if(ind[y] < ind[x])
maxN = max(maxN , findMax(1 , ind[y] + 1 , ind[x]));
print(maxN);
}
int cmp(int a , int b){
return dep[a] < dep[b] ? ind[b] : ind[a];
}
char s[10];
int main(){
int T;
for(T = read() ; T ; T--){
N = read();
memset(head , 0 , sizeof(head));
memset(dep , 0 , sizeof(dep));
memset(start , 0 , sizeof(start));
memset(size , 0 , sizeof(size));
memset(son , 0 , sizeof(son));
memset(fa , 0 , sizeof(fa));
memset(top , 0 , sizeof(top));
memset(ind , 0 , sizeof(ind));
memset(rk , 0 , sizeof(rk));
memset(val , 0 , sizeof(val));
cntEd = ts = 0;
int i;
for(i = 1 ; i < N ; i++){
start[(i << 1) - 1] = read();
start[i << 1] = read();
int c = read();
addEd(start[(i << 1) - 1] , start[i << 1] , c);
addEd(start[i << 1] , start[(i << 1) - 1] , c);
}
dfs1(1 , 0 , 0);
dfs2(1 , 1);
init(1 , 1 , N);
while(scanf("%s" , s) && s[0] != 'D'){
int a = read() , b = read();
if(s[0] == 'Q')
work(a , b);
else
change(1 , cmp(start[a << 1] , start[(a << 1) - 1]) , b);
}
}
return 0;
}
Qtree2
同样先把边权变为较深的点的点权,那么这两个操作都可以通过倍增+维护倍增经过的所有点的点权和完成。
#include
//This code is written by Itst
using namespace std;
inline int read(){
int a = 0;
bool f = 0;
char c = getchar();
while(c != EOF && !isdigit(c)){
if(c == '-')
f = 1;
c = getchar();
}
while(c != EOF && isdigit(c)){
a = (a << 3) + (a << 1) + (c ^ '0');
c = getchar();
}
return f ? -a : a;
}
const int MAXN = 10010;
struct Edge{
int end , upEd , w;
}Ed[MAXN << 1];
int jump[MAXN][21][2] , head[MAXN] , dep[MAXN];
int N , cntEd;
inline void addEd(int a , int b , int c){
Ed[++cntEd].end = b;
Ed[cntEd].upEd = head[a];
Ed[cntEd].w = c;
head[a] = cntEd;
}
void dfs(int x , int f){
jump[x][0][0] = f;
dep[x] = dep[f] + 1;
for(int i = 1 ; jump[x][i - 1][0] ; ++i){
jump[x][i][0] = jump[jump[x][i - 1][0]][i - 1][0];
jump[x][i][1] = jump[x][i - 1][1] + jump[jump[x][i - 1][0]][i - 1][1];
}
for(int i = head[x] ; i ; i = Ed[i].upEd)
if(Ed[i].end != f){
jump[Ed[i].end][0][1] = Ed[i].w;
dfs(Ed[i].end , x);
}
}
inline pair < int , int > LCA(int x , int y){
int sum = 0;
if(dep[x] < dep[y])
swap(x , y);
for(int i = 20 ; i >= 0 ; --i)
if(dep[x] - (1 << i) >= dep[y]){
sum += jump[x][i][1];
x = jump[x][i][0];
}
if(x == y)
return make_pair(x , sum);
for(int i = 20 ; i >= 0 ; --i)
if(jump[x][i][0] != jump[y][i][0]){
sum += jump[x][i][1] + jump[y][i][1];
x = jump[x][i][0];
y = jump[y][i][0];
}
return make_pair(jump[x][0][0] , sum + jump[x][0][1] + jump[y][0][1]);
}
inline int Kth(int x , int y , int k){
int t = LCA(x , y).first;
if(dep[x] - dep[t] + 1 >= k){
--k;
for(int i = 16 ; i >= 0 ; --i)
if(k & (1 << i))
x = jump[x][i][0];
return x;
}
else{
k = dep[x] + dep[y] - (dep[t] << 1) + 1 - k;
for(int i = 16 ; i >= 0 ; --i)
if(k & (1 << i))
y = jump[y][i][0];
return y;
}
}
inline char getc(){
char c = getchar();
while(!isupper(c))
c = getchar();
return c = getchar();
}
int main(){
for(int T = read() ; T ; --T){
memset(head , 0 , sizeof(head));
memset(jump , 0 , sizeof(jump));
cntEd = 0;
N = read();
for(int i = 1 ; i < N ; ++i){
int a = read() , b = read() , c = read();
addEd(a , b , c);
addEd(b , a , c);
}
dfs(1 , 0);
int a , b , c;
bool f = 1;
while(f)
switch(getc()){
case 'I':
printf("%d\n" , LCA(read() , read()).second);
break;
case 'T':
a = read();
b = read();
c = read();
printf("%d\n" , Kth(a , b , c));
break;
default:
f = 0;
}
cout << endl;
}
return 0;
}
Qtree3
一样是树链剖分,线段树上每一个点维护这一段区间中dfn序最大的黑点的dfn序。
#include
#define MAXN 100001
using namespace std;
namespace IO{
const int maxn((1 << 21) + 1);
char ibuf[maxn], *iS, *iT, obuf[maxn], *oS = obuf, *oT = obuf + maxn - 1, c, st[55];
int f, tp;
char Getc() {
return (iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, maxn, stdin), (iS == iT ? EOF : *iS++)) : *iS++);
}
void Flush() {
fwrite(obuf, 1, oS - obuf, stdout);
oS = obuf;
}
void Putc(char x) {
*oS++ = x;
if (oS == oT) Flush();
}
template void Input(Int &x) {
for (f = 1, c = Getc(); c < '0' || c > '9'; c = Getc()) f = c == '-' ? -1 : 1;
for (x = 0; c <= '9' && c >= '0'; c = Getc()) x = (x << 3) + (x << 1) + (c ^ 48);
x *= f;
}
template void Print(Int x) {
if (!x) Putc('0');
if (x < 0) Putc('-'), x = -x;
while (x) st[++tp] = x % 10 + '0', x /= 10;
while (tp) Putc(st[tp--]);
}
void Getstr(char *s, int &l) {
for (c = Getc(); c < 'a' || c > 'z'; c = Getc());
for (l = 0; c <= 'z' && c >= 'a'; c = Getc()) s[l++] = c;
s[l] = 0;
}
void Putstr(const char *s) {
for (int i = 0, n = strlen(s); i < n; ++i) Putc(s[i]);
}
}
using namespace IO;
struct node{
int l , r , minN;
}Tree[MAXN << 2];
struct Edge{
int end , upEd;
}Ed[MAXN << 1];
int son[MAXN] , size[MAXN] , fa[MAXN] , dep[MAXN] , head[MAXN];
int top[MAXN] , ind[MAXN] , rk[MAXN] , N , cntEd , ts;
inline void addEd(int a , int b){
Ed[++cntEd].end = b;
Ed[cntEd].upEd = head[a];
head[a] = cntEd;
}
void dfs1(int dir , int father){
size[dir] = 1;
dep[dir] = dep[fa[dir] = father] + 1;
for(int i = head[dir] ; i ; i = Ed[i].upEd)
if(!dep[Ed[i].end]){
dfs1(Ed[i].end , dir);
size[dir] += size[Ed[i].end];
if(size[son[dir]] < size[Ed[i].end])
son[dir] = Ed[i].end;
}
}
void dfs2(int dir , int t){
top[dir] = t;
rk[ind[dir] = ++ts] = dir;
if(!son[dir])
return;
dfs2(son[dir] , t);
for(int i = head[dir] ; i ; i = Ed[i].upEd)
if(Ed[i].end != son[dir] && Ed[i].end != fa[dir])
dfs2(Ed[i].end , Ed[i].end);
}
inline int min(int a , int b){
return a < b ? a : b;
}
void init(int dir , int l , int r){
Tree[dir].l = l;
Tree[dir].r = r;
if(l == r)
Tree[dir].minN = 999999;
else{
init(dir << 1 , l , (l + r) >> 1);
init(dir << 1 | 1 , ((l + r) >> 1) + 1 , r);
Tree[dir].minN = min(Tree[dir << 1].minN , Tree[dir << 1 | 1].minN);
}
}
void change(int dir , int tar){
if(Tree[dir].l == Tree[dir].r){
Tree[dir].minN = Tree[dir].minN == 999999 ? Tree[dir].l : 999999;
return;
}
if(tar <= (Tree[dir].l + Tree[dir].r) >> 1)
change(dir << 1 , tar);
else
change(dir << 1 | 1 , tar);
Tree[dir].minN = min(Tree[dir << 1].minN , Tree[dir << 1 | 1].minN);
}
int findMin(int dir , int l , int r){
if(Tree[dir].l >= l && Tree[dir].r <= r)
return Tree[dir].minN;
int minN;
if(l <= (Tree[dir].l + Tree[dir].r) >> 1){
minN = findMin(dir << 1 , l , r);
if(minN != 999999)
return minN;
}
if(r > (Tree[dir].l + Tree[dir].r) >> 1)
return findMin(dir << 1 | 1 , l , r);
return 999999;
}
inline int work(int tar){
int minN = 999999;
while(top[tar] != 1){
minN = min(minN , findMin(1 , ind[top[tar]] , ind[tar]));
tar = fa[top[tar]];
}
minN = min(minN , findMin(1 , 1 , ind[tar]));
return minN == 999999 ? -1 : rk[minN];
}
int main(){
int N , M;
Input(N);
Input(M);
for(int i = 1 ; i < N ; i++){
int a , b;
Input(a);
Input(b);
addEd(a , b);
addEd(b , a);
}
dfs1(1 , 0);
dfs2(1 , 1);
init(1 , 1 , N);
while(M--){
int a;
Input(a);
if(a == 0){
Input(a);
change(1 , ind[a]);
}
else{
Input(a);
Print(work(a));
Putc('\n');
}
}
Flush();
return 0;
}
Qtree4
在动态点分的学习笔记里面
Qtree5
仍然是动态点分,对于每一个点维护其所有点分树子树中的白点到当前点的最短距离。修改和查询都暴力跳点分树。
注意到从一个子树中走到分治中心再走回去一定不优,所以并不需要维护到父亲的堆。
#include
#define INF (int)1e9
//This code is written by Itst
using namespace std;
inline int read(){
int a = 0;
bool f = 0;
char c = getchar();
while(c != EOF && !isdigit(c)){
if(c == '-')
f = 1;
c = getchar();
}
while(c != EOF && isdigit(c)){
a = (a << 3) + (a << 1) + (c ^ '0');
c = getchar();
}
return f ? -a : a;
}
const int MAXN = 100010;
struct Edge{
int end , upEd;
}Ed[MAXN << 1];
int head[MAXN] , dep[MAXN] , fir[MAXN] , ST[21][MAXN << 1] , logg2[MAXN << 1] , fa[MAXN][20] , size[MAXN] , dis[MAXN][20];
int N , nowSize , minSize , minInd , cntST , cntEd;
bool vis[MAXN] , col[MAXN];
struct pq{
priority_queue < int , vector < int > , greater < int > > q1 , q2;
inline void maintain(){
while(!q1.empty() && !q2.empty() && q1.top() == q2.top()){
q1.pop();
q2.pop();
}
}
inline void push(int x){
q1.push(x);
}
inline void pop(int x){
q2.push(x);
}
inline int top(){
maintain();
return q1.empty() ? INF : q1.top();
}
}cur[MAXN];
inline void addEd(int a , int b){
Ed[++cntEd].end = b;
Ed[cntEd].upEd = head[a];
head[a] = cntEd;
}
void init_dfs(int x , int pre){
dep[x] = dep[pre] + 1;
fir[x] = ++cntST;
ST[0][cntST] = x;
for(int i = head[x] ; i ; i = Ed[i].upEd)
if(Ed[i].end != pre){
init_dfs(Ed[i].end , x);
ST[0][++cntST] = x;
}
}
inline int cmp(int a , int b){
return dep[a] < dep[b] ? a : b;
}
void init_st(){
for(int i = 2 ; i <= cntST ; ++i)
logg2[i] = logg2[i >> 1] + 1;
for(int i = 1 ; 1 << i <= cntST ; ++i)
for(int j = 1 ; j + (1 << i) - 1 <= cntST ; ++j)
ST[i][j] = cmp(ST[i - 1][j] , ST[i - 1][j + (1 << (i - 1))]);
}
inline int LCA(int x , int y){
x = fir[x];
y = fir[y];
if(x > y)
swap(x , y);
int t = logg2[y - x + 1];
return cmp(ST[t][x] , ST[t][y - (1 << t) + 1]);
}
inline int calcLen(int x , int y){
return dep[x] + dep[y] - (dep[LCA(x , y)] << 1);
}
void getSize(int x){
vis[x] = 1;
++nowSize;
for(int i = head[x] ; i ; i = Ed[i].upEd)
if(!vis[Ed[i].end])
getSize(Ed[i].end);
vis[x] = 0;
}
void getRoot(int x){
int maxN = 0;
vis[x] = size[x] = 1;
for(int i = head[x] ; i ; i = Ed[i].upEd)
if(!vis[Ed[i].end]){
getRoot(Ed[i].end);
size[x] += size[Ed[i].end];
maxN = max(maxN , size[Ed[i].end]);
}
maxN = max(maxN , nowSize - size[x]);
if(maxN < minSize){
minSize = maxN;
minInd = x;
}
vis[x] = 0;
}
void init_dfz(int x , int p){
nowSize = 0;
minSize = 0x7fffffff;
getSize(x);
getRoot(x);
x = minInd;
vis[x] = 1;
fa[x][0] = p;
for(int i = 0 ; fa[x][i] ; ++i){
fa[x][i + 1] = fa[fa[x][i]][0];
dis[x][i] = calcLen(x , fa[x][i]);
}
for(int i = head[x] ; i ; i = Ed[i].upEd)
if(!vis[Ed[i].end])
init_dfz(Ed[i].end , x);
}
void init(){
init_dfs(1 , 0);
init_st();
init_dfz(1 , 0);
}
inline int query(int x){
int minN = cur[x].top();
for(int i = 0 ; fa[x][i] ; ++i)
minN = min(minN , cur[fa[x][i]].top() + dis[x][i]);
return minN == INF ? -1 : minN;
}
inline void modify(int x){
vis[x] ^= 1;
vis[x] ? cur[x].pop(0) : cur[x].push(0);
for(int i = 0 ; fa[x][i] ; ++i)
vis[x] ? cur[fa[x][i]].pop(dis[x][i]) : cur[fa[x][i]].push(dis[x][i]);
}
int main(){
N = read();
for(int i = 1 ; i < N ; ++i){
int a = read() , b = read();
addEd(a , b);
addEd(b , a);
}
init();
for(int M = read() ; M ; --M)
if(read())
printf("%d\n" , query(read()));
else
modify(read());
return 0;
}
Qtree6
考虑LCT。维护点的颜色连通块似乎不好做,因为是否连通实际上是和边相关的一个东西。我们考虑每一个点,当这个点是黑色的时候在黑色的LCT上将它和它的父亲连边,否则在白色的LCT上连边。这样对于黑白两色的LCT,去掉每一个连通块的root后,剩余的每一个连通块就是当前颜色的一个颜色连通块。
那么我们按照这个进行Link、Cut,并在每一次询问的时候先access保证当前点在整棵树的实链上,然后findroot并将root splay至根,最后输出根的右儿子的size即可。
值得注意的是在Link和Cut中不能够进行平常需要在其中进行的makeroot操作,因为这样会改变每一个连通块的根。我们可以这样修改:
Link:每一次进行Link操作的点都一定是当前连通块的根,所以直接splay至LCT的根即可;
Cut:因为每一次要cut的边连接的两个点的父子关系确定,所以可以不进行makeroot,直接对子节点access、splay并去掉对应的边。
至于如何维护子树size,可以维护一个变量表示当前点的虚子树size和,不难发现只有在link和access的时候会改变这个量,这样就可以进行整个子树的维护。值得注意的是维护虚子树信息在link的时候要将父节点先access、splay到LCT的根,这样可以避免更新子树大小之后的一系列pushup。
#include
using namespace std;
int read(){
int a = 0; char c = getchar(); bool f = 0;
while(!isdigit(c)){f = c == '-'; c = getchar();}
while(isdigit(c)){
a = a * 10 + c - 48; c = getchar();
}
return f ? -a : a;
}
const int _ = 2e5 + 3;
int fa[_] , ch[_][2] , sz[_] , vir[_]; bool rmrk[_];
struct Edge{int end , upEd;}Ed[_ << 1];
int pre[_] , head[_] , col[_] , cntEd , N , M;
void addEd(int a , int b){Ed[++cntEd] = (Edge){b , head[a]}; head[a] = cntEd;}
void dfs(int x , int p){pre[x] = p; for(int i = head[x] ; i ; i = Ed[i].upEd) if(Ed[i].end != p) dfs(Ed[i].end , x);}
bool nroot(int x){return ch[fa[x]][0] == x || ch[fa[x]][1] == x;}
bool son(int x){return ch[fa[x]][1] == x;}
void up(int x){sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + 1 + vir[x];}
void rot(int x){
bool f = son(x); int y = fa[x] , z = fa[y] , w = ch[x][f ^ 1];
fa[x] = z; if(nroot(y)) ch[z][son(y)] = x;
fa[y] = x; ch[x][f ^ 1] = y;
ch[y][f] = w; if(w) fa[w] = y;
up(y);
}
void splay(int x){while(nroot(x)){if(nroot(fa[x])) rot(son(x) == son(fa[x]) ? fa[x] : x); rot(x);} up(x);}
void access(int x){
for(int y = 0 ; x ; y = x , x = fa[x]){
splay(x); vir[x] = vir[x] - sz[y] + sz[ch[x][1]];
ch[x][1] = y; up(x);
}
}
int fdrt(int x){access(x); splay(x); while(ch[x][0]) x = ch[x][0]; splay(x); return x;}
void link(int x){splay(x); int y = fa[x] = pre[x]; access(y); splay(y); vir[y] += sz[x]; up(y);}
void cut(int x){access(x); splay(x); ch[x][0] = fa[ch[x][0]] = 0; up(x);}
int qry(int x){x += col[x] * N; access(x); splay(x); splay(x = fdrt(x)); return sz[ch[x][1]];}
int main(){
N = read();
for(int i = 1 ; i < N ; ++i){int a = read() , b = read(); addEd(a , b); addEd(b , a);}
dfs(1 , 0); pre[1] = ++N;
for(int i = 1 ; i <= 2 * N ; ++i) sz[i] = 1;
for(int i = 1 ; i < N ; ++i){link(i); pre[i + N] = pre[i] + N;}
for(M = read() ; M ; --M)
if(read()){int id = read(); cut(id + N * col[id]); link(id + N * (col[id] ^= 1));}
else printf("%d\n" , qry(read()));
return 0;
}
Qtree7
考虑在Qtree6的基础上支持子树最值。
考虑维护所有虚子树的最大值,但是注意到我们还可能进行虚实转换使得虚子树变成实子树、不对当前点的虚子树max产生贡献,那么就不能够只使用一个标记维护max。
考虑使用multiset维护一个点的虚子树的max构成的集合,那么在虚实更换的时候对这个multiset进行插入、删除即可进行更新。
复杂度\(O(qlog^2n)\)
#include
using namespace std;
int read(){
int a = 0; char c = getchar(); bool f = 0;
while(!isdigit(c)){f = c == '-'; c = getchar();}
while(isdigit(c)){
a = a * 10 + c - 48; c = getchar();
}
return f ? -a : a;
}
const int _ = 2e5 + 7;
struct Edge{int end , upEd;}Ed[_ << 1];
int head[_] , N , cntEd , pre[_] , col[_];
void addEd(int a , int b){Ed[++cntEd] = (Edge){b , head[a]}; head[a] = cntEd;}
void dfs(int x , int p){pre[x] = p; for(int i = head[x] ; i ; i = Ed[i].upEd) if(Ed[i].end != p) dfs(Ed[i].end , x);}
int fa[_] , ch[_][2] , val[_] , mx[_]; multiset < int > vir[_];
bool nroot(int x){return ch[fa[x]][0] == x || ch[fa[x]][1] == x;}
bool son(int x){return ch[fa[x]][1] == x;}
void up(int x){mx[x] = max(max(val[x] , vir[x].size() ? *--vir[x].end() : (int)-2e9) , max(mx[ch[x][0]] , mx[ch[x][1]]));}
void rot(int x){
bool f = son(x); int y = fa[x] , z = fa[y] , w = ch[x][f ^ 1];
fa[x] = z; if(nroot(y)) ch[z][son(y)] = x;
fa[y] = x; ch[x][f ^ 1] = y;
ch[y][f] = w; if(w) fa[w] = y;
up(y);
}
void splay(int x){while(nroot(x)){if(nroot(fa[x])) rot(son(x) == son(fa[x]) ? fa[x] : x); rot(x);} up(x);}
void access(int x){
for(int y = 0 ; x ; y = x , x = fa[x]){
splay(x); if(ch[x][1]) vir[x].insert(mx[ch[x][1]]);
if(y) vir[x].erase(vir[x].find(mx[y]));
ch[x][1] = y; up(x);
}
}
int fdrt(int x){access(x); splay(x); while(ch[x][0]) x = ch[x][0]; splay(x); return x;}
void link(int x){splay(x); int t = pre[x]; access(t); splay(t); fa[x] = t; vir[t].insert(mx[x]); up(t);}
void cut(int x){access(x); splay(x); ch[x][0] = fa[ch[x][0]] = 0; up(x);}
void mdy(int x , int w){access(x); splay(x); val[x] = w; up(x);}
int qry(int x){access(x); splay(x); x = fdrt(x); return mx[ch[x][1]];}
int main(){
mx[0] = -2e9; N = read();
for(int i = 1 ; i < N ; ++i){int a = read() , b = read(); addEd(a , b); addEd(b , a);}
dfs(1 , 0); pre[1] = ++N;
for(int i = 1 ; i < N ; ++i) col[i] = read();
for(int i = 1 ; i < N ; ++i) val[i] = val[i + N] = mx[i] = mx[i + N] = read();
for(int i = 1 ; i < N ; ++i) pre[i + N] = pre[i] + N;
for(int i = 1 ; i < N ; ++i) link(i + col[i] * N);
for(int M = read() ; M ; --M){
int tp = read() , u = read();
if(tp == 0) printf("%d\n" , qry(u + col[u] * N));
else if(tp == 1){cut(u + col[u] * N); link(u + (col[u] ^= 1) * N);}
else{int w = read(); mdy(u , w); mdy(u + N , w);}
}
return 0;
}