2023寒假集训5(1月9日)-算法竞赛-4.10树链剖分-cf>1800 题解
A - 轻重链剖分/树链剖分
树链剖分模板题
#include <bits/stdc++.h>
#define int long long
const int N = 1e6 + 10;
using namespace std;
//线段树
int n,m,root,mod,a[N];
int w[N];
struct node{
int l,r,sum,lz;
}tree[N];
void lazy(int k,int v)
{
tree[k].sum+=(tree[k].r-tree[k].l+1)*v;tree[k].sum%=mod;
if(tree[k].r!=tree[k].l)tree[k].lz+=v;
}
void push_down(int k)
{
lazy(k<<1,tree[k].lz);
lazy(k<<1|1,tree[k].lz);
tree[k].lz=0;
}
void build(int k,int l,int r)
{
tree[k].l=l;tree[k].r=r;tree[k].lz=0;
if(l==r)
{
tree[k].sum=w[l];tree[k].sum%=mod;
return ;
}
int midd=l+r>>1;
build(k<<1,l,midd);
build(k<<1|1,midd+1,r);
tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;tree[k].sum%=mod;
}
void update(int k,int l,int r,int v)
{
if(tree[k].l>=l&&tree[k].r<=r)
return lazy(k,v);
if(tree[k].lz)push_down(k);
int midd=tree[k].l+tree[k].r>>1;
if(l<=midd)update(k<<1,l,r,v);
if(r>midd)update(k<<1|1,l,r,v);
tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;tree[k].sum%=mod;
}
int query(int k,int l,int r)
{
if(tree[k].l>=l&&tree[k].r<=r)
return tree[k].sum%=mod;;
if(tree[k].lz)push_down(k);
int midd=tree[k].l+tree[k].r>>1,tans=0;
if(l<=midd)tans+=query(k<<1,l,r);
if(r>midd)tans+=query(k<<1|1,l,r);
tans%=mod;
return tans;
}
//树链剖分
struct Edge{
int to,next;
}e[N];
int head[N]={0},cnt=0;
void add(int u, int v) {
e[++cnt].next = head[u];
e[cnt].to = v;
head[u] = cnt;
}
int dep[N],sz[N],son[N],top[N],fa[N],id[N],num=0;
void dfs1(int x,int fx)
{
dep[x]=dep[fx]+1;
fa[x]=fx;
sz[x]=1;
for (int i = head[x]; i; i = e[i].next)
{
if(e[i].to==fx)continue;
dfs1(e[i].to,x);
sz[x]+=sz[e[i].to];
if(!son[x]||sz[son[x]]<sz[e[i].to])
son[x]=e[i].to;
}
}
void dfs2(int x,int topx)
{
id[x]=++num;
w[num]=a[x];
top[x]=topx;
if(!son[x])return ;
dfs2(son[x],topx);
for (int i = head[x]; i; i = e[i].next)
if(e[i].to!=fa[x]&&e[i].to!=son[x])
dfs2(e[i].to,e[i].to);
}
int LCA(int x,int y)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])swap(x,y);
x=fa[top[x]];
}
return dep[x]<dep[y]?x:y;
}
void update_range(int x,int y,int v)//对于树上x到y节点最短路径上的所有节点值都加v
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])swap(x,y);
update(1,id[top[x]],id[x],v);
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
update(1,id[x],id[y],v);
}
int query_range(int x,int y)//查询树上x到y节点最短路径上的所有节点值之和
{
int tans=0;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])swap(x,y);
tans+=query(1,id[top[x]],id[x]);
tans%=mod;
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
tans+=query(1,id[x],id[y]);
tans%=mod;
return tans;
}
void update_tree(int x,int v)//对于以x为根节点的子树内所有节点值都加v
{
update(1,id[x],id[x]+sz[x]-1,v);
}
int query_tree(int x)//查询以x为根节点的子树内所有节点值之和
{
return query(1,id[x],id[x]+sz[x]-1);
}
signed main() {
scanf("%lld%lld%lld%lld",&n,&m,&root,&mod);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
int u,v;
for(int i=1;i<n;i++)
{
scanf("%lld%lld",&u,&v);
add(u,v);add(v,u);
}
dfs1(root,0);dfs2(root,root);
build(1,1,n);
int t,x,y,z;
while(m--)
{
scanf("%lld",&t);
switch(t)
{
case 1:
scanf("%lld%lld%lld",&x,&y,&z);
update_range(x,y,z);
break;
case 2:
scanf("%lld%lld",&x,&y);
printf("%lld\n",query_range(x,y));
break;
case 3:
scanf("%lld%lld",&x,&z);
update_tree(x,z);
break;
case 4:
scanf("%lld",&x);
printf("%lld\n",query_tree(x));
break;
}
}
return 0;
}
B - 染色
染色问题
本题关键在于如何处理染色修改操作以及区间合并
可以通过在线段树每个点所维护的信息中增加左右端点颜色的记录即可
并且我们在区间更新以及懒标记下移更新过程中需同时更新区间左右端点的单点颜色进行记录,后续查询路径颜色总数时需对于查询过程中所跳跃的轻边进行判断是否存在相邻的同色颜色块被重复计数(可证明查询过程中所跳跃的每条轻边两端点均在重链查询中已更新单点颜色)
#include <bits/stdc++.h>
#define int long long
const int N = 1e6 + 10;
using namespace std;
int n,m,root,mod,a[N];
//线段树
int w[N];
struct node{//新增记录左右端点颜色,用于判断区间合并时是否存在重复计算
int l,r,cl,cr,sum,lz;
}tree[N];
void lazy(int k,int v)
{
tree[k].sum=1;tree[k].cl=v;tree[k].cr=v;
w[tree[k].l]=w[tree[k].r]=v;//更新区间左右端点颜色
if(tree[k].r!=tree[k].l)tree[k].lz=v;
}
void push_down(int k)
{
lazy(k<<1,tree[k].lz);
lazy(k<<1|1,tree[k].lz);
tree[k].lz=0;
}
void build(int k,int l,int r)
{
tree[k].l=l;tree[k].r=r;tree[k].lz=0;
if(l==r)
{
tree[k].sum=1;tree[k].cl=tree[k].cr=w[l];
return ;
}
int midd=l+r>>1;
build(k<<1,l,midd);
build(k<<1|1,midd+1,r);
tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum-(tree[k<<1].cr==tree[k<<1|1].cl);//判断区间合并时是否存在重复计算
tree[k].cl=tree[k<<1].cl;tree[k].cr=tree[k<<1|1].cr;
}
void update(int k,int l,int r,int v)
{
if(tree[k].l>=l&&tree[k].r<=r)
return lazy(k,v);
if(tree[k].lz)push_down(k);
int midd=tree[k].l+tree[k].r>>1;
if(l<=midd)update(k<<1,l,r,v);
if(r>midd)update(k<<1|1,l,r,v);
tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum-(tree[k<<1].cr==tree[k<<1|1].cl);//判断区间合并时是否存在重复计算
w[tree[k].l]=tree[k].cl=tree[k<<1].cl;w[tree[k].r]=tree[k].cr=tree[k<<1|1].cr;//更新区间左右端点颜色
}
int query(int k,int l,int r)
{
if(tree[k].l>=l&&tree[k].r<=r)
return tree[k].sum;
if(tree[k].lz)push_down(k);
int midd=tree[k].l+tree[k].r>>1,tans=0;
if(l<=midd)tans+=query(k<<1,l,r);
if(r>midd)tans+=query(k<<1|1,l,r);
tans-=(l<=midd&&r>midd&&tree[k<<1].cr==tree[k<<1|1].cl);//判断是否查询了两个相邻区间,若有继续判断两区间合并时是否存在重复计算
return tans;
}
//树链剖分
struct Edge{
int to,next;
}e[N];
int head[N]={0},cnt=0;
void add(int u, int v) {
e[++cnt].next = head[u];
e[cnt].to = v;
head[u] = cnt;
}
int dep[N],sz[N],son[N],top[N],fa[N],id[N],num=0;
void dfs1(int x,int fx)
{
dep[x]=dep[fx]+1;
fa[x]=fx;
sz[x]=1;
for (int i = head[x]; i; i = e[i].next)
{
if(e[i].to==fx)continue;
dfs1(e[i].to,x);
sz[x]+=sz[e[i].to];
if(!son[x]||sz[son[x]]<sz[e[i].to])
son[x]=e[i].to;
}
}
void dfs2(int x,int topx)
{
id[x]=++num;
w[num]=a[x];
top[x]=topx;
if(!son[x])return ;
dfs2(son[x],topx);
for (int i = head[x]; i; i = e[i].next)
if(e[i].to!=fa[x]&&e[i].to!=son[x])
dfs2(e[i].to,e[i].to);
}
void update_range(int x,int y,int v)//对于树上x到y节点最短路径上的所有节点值都加v
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])swap(x,y);
update(1,id[top[x]],id[x],v);
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
update(1,id[x],id[y],v);
}
int query_range(int x,int y)//查询树上x到y节点最短路径上的所有节点值之和
{
int tans=0;
vector<pair<int,int>>ve;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])swap(x,y);
tans+=query(1,id[top[x]],id[x]);
ve.push_back({top[x],fa[top[x]]});
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
tans+=query(1,id[x],id[y]);
for(auto i:ve)//判断所跳跃的几条轻边中,是否存在重链的链头与其父亲节点颜色相同,即出现同一颜色在相邻两段中存在重复计算
tans-=w[id[i.first]]==w[id[i.second]];
return tans;
}
signed main() {
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
int u,v;
for(int i=1;i<n;i++)
{
scanf("%lld%lld",&u,&v);
add(u,v);add(v,u);
}
dfs1(1,0);dfs2(1,1);
build(1,1,n);
int a,b,c;
string s;
while(m--)
{
cin>>s;
if(s[0]=='C')
{
scanf("%lld%lld%lld",&a,&b,&c);
update_range(a,b,c);
}
else
{
scanf("%lld%lld",&a,&b);
printf("%lld\n",query_range(a,b));
}
}
return 0;
}
C - 软件包管理器
将
i
i
i 号软件包以及它所依赖的
a
i
a_{i}
ai 号软件包连边建树
由于只有
0
0
0 号软件包无所依赖的软件包,因此安装
x
x
x 号软件包所需要改变安装状态的软件包数量等于树上
x
x
x 号节点与
0
0
0 号路径上未安装的软件包数量
卸载
x
x
x 号软件包所需要改变安装状态的软件包数量等于树上
x
x
x 号节点及其子树上已安装的软件包数量
将安装看作路径上每个节点权值
+
1
+1
+1 ,卸载看作路径上每个节点权值
−
1
-1
−1,注意处理边界不能出现某点权值
<
0
<0
<0 或
>
1
>1
>1
#include <bits/stdc++.h>
#define int long long
const int N = 1e6 + 10;
using namespace std;
int n,f[N];
//线段树
int w[N];
struct node{
int l,r,sum,lz;
}tree[N];
void lazy(int k,int v)
{
if(v>0)tree[k].sum=tree[k].r-tree[k].l+1;//注意边界
else tree[k].sum=0;
if(tree[k].r!=tree[k].l)tree[k].lz=v;
}
void push_down(int k)
{
lazy(k<<1,tree[k].lz);
lazy(k<<1|1,tree[k].lz);
tree[k].lz=0;
}
void build(int k,int l,int r)
{
tree[k].l=l;tree[k].r=r;tree[k].lz=0;
if(l==r)
{
tree[k].sum=w[l];
return ;
}
int midd=l+r>>1;
build(k<<1,l,midd);
build(k<<1|1,midd+1,r);
tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
}
void update(int k,int l,int r,int v)
{
if(tree[k].l>=l&&tree[k].r<=r)
return lazy(k,v);
if(tree[k].lz)push_down(k);
int midd=tree[k].l+tree[k].r>>1;
if(l<=midd)update(k<<1,l,r,v);
if(r>midd)update(k<<1|1,l,r,v);
tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
}
int query(int k,int l,int r)
{
if(tree[k].l>=l&&tree[k].r<=r)
return tree[k].sum;
if(tree[k].lz)push_down(k);
int midd=tree[k].l+tree[k].r>>1,tans=0;
if(l<=midd)tans+=query(k<<1,l,r);
if(r>midd)tans+=query(k<<1|1,l,r);
return tans;
}
//树链剖分
struct Edge{
int to,next;
}e[N];
int head[N]={0},cnt=0;
void add(int u, int v) {
e[++cnt].next = head[u];
e[cnt].to = v;
head[u] = cnt;
}
int dep[N],sz[N],son[N],top[N],fa[N],id[N],num=0;
void dfs1(int x,int fx)
{
dep[x]=dep[fx]+1;
fa[x]=fx;
sz[x]=1;
for (int i = head[x]; i; i = e[i].next)
{
if(e[i].to==fx)continue;
dfs1(e[i].to,x);
sz[x]+=sz[e[i].to];
if(!son[x]||sz[son[x]]<sz[e[i].to])
son[x]=e[i].to;
}
}
void dfs2(int x,int topx)
{
id[x]=++num;
w[num]=0;
top[x]=topx;
if(!son[x])return ;
dfs2(son[x],topx);
for (int i = head[x]; i; i = e[i].next)
if(e[i].to!=fa[x]&&e[i].to!=son[x])
dfs2(e[i].to,e[i].to);
}
void update_range(int x,int y,int v)//对于树上x到y节点最短路径上的所有节点值都加v
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])swap(x,y);
update(1,id[top[x]],id[x],v);
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
update(1,id[x],id[y],v);
}
int query_range(int x,int y)//查询树上x到y节点最短路径上的所有节点值之和
{
int tans=0;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])swap(x,y);
tans+=query(1,id[top[x]],id[x]);
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
tans+=query(1,id[x],id[y]);
return tans;
}
void update_tree(int x,int v)//对于以x为根节点的子树内所有节点值都加v
{
update(1,id[x],id[x]+sz[x]-1,v);
}
int query_tree(int x)//查询以x为根节点的子树内所有节点值之和
{
return query(1,id[x],id[x]+sz[x]-1);
}
signed main() {
scanf("%lld",&n);
int u,v;
for(int i=2;i<=n;i++)
{
scanf("%lld",&f[i]);f[i]++;
add(i,f[i]);add(f[i],i);
}
dfs1(1,0);dfs2(1,1);
build(1,1,n);
int m,x;
string s;
scanf("%lld",&m);
while(m--)
{
cin>>s;
scanf("%lld",&x);x++;
if(s=="install")//将安装看作区间+1操作
{
printf("%lld\n",dep[x]-query_range(1,x));
update_range(0,x,1);
}
else //将卸载看作区间-1操作
{
printf("%lld\n",query_tree(x));
update_tree(x,-1);
}
}
return 0;
}
D - 树的统计
树链剖分模板题,相较于 A A A 题去除区间修改,更换为单点修改,新增区间最值查询
#include <bits/stdc++.h>
#define int long long
const int N = 1e6 + 10;
using namespace std;
int n,a[N];
//线段树
int w[N];
struct node{
int l,r,sum,mx;
}tree[N];
void build(int k,int l,int r)
{
tree[k].l=l;tree[k].r=r;
if(l==r)
{
tree[k].sum=w[l];tree[k].mx=w[l];
return ;
}
int midd=l+r>>1;
build(k<<1,l,midd);
build(k<<1|1,midd+1,r);
tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
tree[k].mx=max(tree[k<<1].mx,tree[k<<1|1].mx);
}
void update(int k,int l,int r,int v)
{
if(tree[k].l>=l&&tree[k].r<=r)
{tree[k].mx=v;tree[k].sum=v;return ;}
int midd=tree[k].l+tree[k].r>>1;
if(l<=midd)update(k<<1,l,r,v);
if(r>midd)update(k<<1|1,l,r,v);
tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
tree[k].mx=max(tree[k<<1].mx,tree[k<<1|1].mx);
}
int query_sum(int k,int l,int r)
{
if(tree[k].l>=l&&tree[k].r<=r)
return tree[k].sum;
int midd=tree[k].l+tree[k].r>>1,tans=0;
if(l<=midd)tans+=query_sum(k<<1,l,r);
if(r>midd)tans+=query_sum(k<<1|1,l,r);
return tans;
}
int query_max(int k,int l,int r)
{
if(tree[k].l>=l&&tree[k].r<=r)
return tree[k].mx;
int midd=tree[k].l+tree[k].r>>1,tans=LONG_LONG_MIN;
if(l<=midd)tans=max(tans,query_max(k<<1,l,r));
if(r>midd)tans=max(tans,query_max(k<<1|1,l,r));
return tans;
}
//树链剖分
struct Edge{
int to,next;
}e[N];
int head[N]={0},cnt=0;
void add(int u, int v) {
e[++cnt].next = head[u];
e[cnt].to = v;
head[u] = cnt;
}
int dep[N],sz[N],son[N],top[N],fa[N],id[N],num=0;
void dfs1(int x,int fx)
{
dep[x]=dep[fx]+1;
fa[x]=fx;
sz[x]=1;
for (int i = head[x]; i; i = e[i].next)
{
if(e[i].to==fx)continue;
dfs1(e[i].to,x);
sz[x]+=sz[e[i].to];
if(!son[x]||sz[son[x]]<sz[e[i].to])
son[x]=e[i].to;
}
}
void dfs2(int x,int topx)
{
id[x]=++num;
w[num]=a[x];
top[x]=topx;
if(!son[x])return ;
dfs2(son[x],topx);
for (int i = head[x]; i; i = e[i].next)
if(e[i].to!=fa[x]&&e[i].to!=son[x])
dfs2(e[i].to,e[i].to);
}
int query_range_sum(int x,int y)//查询树上x到y节点最短路径上的所有节点值之和
{
int tans=0;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])swap(x,y);
tans+=query_sum(1,id[top[x]],id[x]);
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
tans+=query_sum(1,id[x],id[y]);
return tans;
}
int query_range_max(int x,int y)//查询树上x到y节点最短路径上的所有节点值的最大值
{
int tans=LONG_LONG_MIN;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])swap(x,y);
tans=max(tans,query_max(1,id[top[x]],id[x]));
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
tans=max(tans,query_max(1,id[x],id[y]));
return tans;
}
signed main() {
scanf("%lld",&n);
int u,v;
for(int i=1;i<n;i++)
{
scanf("%lld%lld",&u,&v);
add(u,v);add(v,u);
}
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
dfs1(1,0);dfs2(1,1);
build(1,1,n);
int m;
string s;
scanf("%lld",&m);
while(m--)
{
cin>>s;
scanf("%lld%lld",&u,&v);
if(s[0]=='C')
update(1,id[u],id[u],v);
else if(s[1]=='M')
printf("%lld\n",query_range_max(u,v));
else printf("%lld\n",query_range_sum(u,v));
}
return 0;
}
E - 树上操作
树链剖分模板题,相较于 A A A 题去除区间修改,更换为单点修改
#include <bits/stdc++.h>
#define int long long
const int N = 1e6 + 10;
using namespace std;
int n,m,root,a[N];
//线段树
int w[N];
struct node{
int l,r,sum,lz;
}tree[N];
void lazy(int k,int v)
{
tree[k].sum+=(tree[k].r-tree[k].l+1)*v;
if(tree[k].r!=tree[k].l)tree[k].lz+=v;
}
void push_down(int k)
{
lazy(k<<1,tree[k].lz);
lazy(k<<1|1,tree[k].lz);
tree[k].lz=0;
}
void build(int k,int l,int r)
{
tree[k].l=l;tree[k].r=r;tree[k].lz=0;
if(l==r)
{
tree[k].sum=w[l];
return ;
}
int midd=l+r>>1;
build(k<<1,l,midd);
build(k<<1|1,midd+1,r);
tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
}
void update(int k,int l,int r,int v)
{
if(tree[k].l>=l&&tree[k].r<=r)
return lazy(k,v);
if(tree[k].lz)push_down(k);
int midd=tree[k].l+tree[k].r>>1;
if(l<=midd)update(k<<1,l,r,v);
if(r>midd)update(k<<1|1,l,r,v);
tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
}
int query(int k,int l,int r)
{
if(tree[k].l>=l&&tree[k].r<=r)
return tree[k].sum;
if(tree[k].lz)push_down(k);
int midd=tree[k].l+tree[k].r>>1,tans=0;
if(l<=midd)tans+=query(k<<1,l,r);
if(r>midd)tans+=query(k<<1|1,l,r);
return tans;
}
//树链剖分
struct Edge{
int to,next;
}e[N];
int head[N]={0},cnt=0;
void add(int u, int v) {
e[++cnt].next = head[u];
e[cnt].to = v;
head[u] = cnt;
}
int dep[N],sz[N],son[N],top[N],fa[N],id[N],num=0;
void dfs1(int x,int fx)
{
dep[x]=dep[fx]+1;
fa[x]=fx;
sz[x]=1;
for (int i = head[x]; i; i = e[i].next)
{
if(e[i].to==fx)continue;
dfs1(e[i].to,x);
sz[x]+=sz[e[i].to];
if(!son[x]||sz[son[x]]<sz[e[i].to])
son[x]=e[i].to;
}
}
void dfs2(int x,int topx)
{
id[x]=++num;
w[num]=a[x];
top[x]=topx;
if(!son[x])return ;
dfs2(son[x],topx);
for (int i = head[x]; i; i = e[i].next)
if(e[i].to!=fa[x]&&e[i].to!=son[x])
dfs2(e[i].to,e[i].to);
}
void update_range(int x,int y,int v)//对于树上x到y节点最短路径上的所有节点值都加v
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])swap(x,y);
update(1,id[top[x]],id[x],v);
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
update(1,id[x],id[y],v);
}
int query_range(int x,int y)//查询树上x到y节点最短路径上的所有节点值之和
{
int tans=0;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])swap(x,y);
tans+=query(1,id[top[x]],id[x]);
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
tans+=query(1,id[x],id[y]);
return tans;
}
void update_tree(int x,int v)//对于以x为根节点的子树内所有节点值都加v
{
update(1,id[x],id[x]+sz[x]-1,v);
}
int query_tree(int x)//查询以x为根节点的子树内所有节点值之和
{
return query(1,id[x],id[x]+sz[x]-1);
}
signed main() {
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
int u,v;
for(int i=1;i<n;i++)
{
scanf("%lld%lld",&u,&v);
add(u,v);add(v,u);
}
dfs1(1,0);dfs2(1,1);
build(1,1,n);
int t,x,y,z;
while(m--)
{
scanf("%lld",&t);
switch(t)
{
case 1:
scanf("%lld%lld",&x,&z);
update_range(x,x,z);
break;
case 2:
scanf("%lld%lld",&x,&z);
update_tree(x,z);
break;
case 3:
scanf("%lld",&x);
printf("%lld\n",query_range(x,1));
break;
}
}
return 0;
}