Query on a tree
前言
这个系列的数据结构题太经典了,不得不补一下。
之前模拟赛考到过 Qtree4,直接莽了个假的点分治上去,人都傻了。
个人认为 Qtree 都不是很难。(除了 Qtree4)
Qtree1
题目描述
给定一棵
CHANGE i t
把第 条边的边权变成QUERY a b
输出从 到 的路径上最大的边权。
题解
这个就是树剖或 LCT 的板子题了。树剖在线段树上维护最大值;LCT维护 splay 上最大值就行。
代码
有亿点点久远了,将就着看吧
这个是树剖版本的。
#include<bits/stdc++.h>
#define ls (pos<<1)
#define rs (pos<<1|1)
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){f=ch=='-'?-1:f;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int n,m;
struct edge
{
int u,v,nxt,w;
}e[200005];
int head[100005],cnt=1;
void addedge(int u,int v,int w)
{
e[++cnt].v=v;
e[cnt].u=u;
e[cnt].w=w;
e[cnt].nxt=head[u];
head[u]=cnt;
}
struct tree
{
int val,top,fa,son,siz,id,dep;
}a[100005];
void dfs1(int x,int f)
{
a[x].fa=f;a[x].siz=1;
a[x].dep=a[f].dep+1;
int maxsiz=0;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].v;
if(y==f)continue;
dfs1(y,x);a[x].siz+=a[y].siz;a[y].val=e[i].w;
if(a[y].siz>maxsiz)a[x].son=y,maxsiz=a[y].siz;
}
}
int tim=0;
int t[100005];
void dfs2(int x,int tp)
{
a[x].id=++tim;t[tim]=a[x].val;a[x].top=tp;
if(a[x].son)dfs2(a[x].son,tp);
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].v;
if(y==a[x].fa||y==a[x].son)continue;
dfs2(y,y);
}
}
int mx[5000005];
void build(int pos,int l,int r)
{
if(l==r){mx[pos]=t[l];return;}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
mx[pos]=max(mx[ls],mx[rs]);
}
void change(int pos,int l,int r,int x,int k)
{
if(l==r){mx[pos]=k;return;}
int mid=(l+r)>>1;
if(x<=mid)change(ls,l,mid,x,k);
else change(rs,mid+1,r,x,k);
mx[pos]=max(mx[ls],mx[rs]);
}
int query(int pos,int l,int r,int L,int R)
{
if(L<=l&&r<=R)return mx[pos];
int mid=(l+r)>>1,res=0;
if(L<=mid)res=max(res,query(ls,l,mid,L,R));
if(R>mid)res=max(res,query(rs,mid+1,r,L,R));
return res;
}
int solve(int x,int y)
{
int ans=0;
while(a[x].top!=a[y].top)
{
if(a[a[x].top].dep<a[a[y].top].dep)swap(x,y);
ans=max(ans,query(1,1,n,a[a[x].top].id,a[x].id));
x=a[a[x].top].fa;
}
if(x==y)return ans;
if(a[x].dep>a[y].dep)swap(x,y);
//cout<<a[x].id+1<<" "<<a[y].id<<endl;
ans=max(ans,query(1,1,n,a[x].id+1,a[y].id));
return ans;
}
int main()
{
n=read();
for(int i=1;i<n;++i)
{
int u=read(),v=read(),w=read();
addedge(u,v,w),addedge(v,u,w);
}
dfs1(1,0);dfs2(1,1);
build(1,1,n);string s=" ";
while(cin>>s)
{
if(s=="DONE")break;
int x=read(),y=read();
if(s=="QUERY") printf("%d\n",solve(x,y));
else
{
int u=e[x<<1].u,v=e[x<<1].v;
if(a[u].dep<a[v].dep)swap(u,v);
change(1,1,n,a[u].id,y);
}
}
return 0;
}
Qtree2
题目描述
给定一棵n个点的树,边具有边权。要求作以下操作:
-
DIST a b
询问点 至点 路径上的边权之和 -
KTH a b k
询问点 至点 有向路径上的第 个点的编号
题解
很水,第一个很好处理,维护
两种方法时间都是
代码
LCT版本,好写好调。(只是常数大了亿点点)
#include<bits/stdc++.h>
#define pc(x) putchar(x)
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){f=ch=='-'?-1:f;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
void write(int x)
{
if(x<0){x=-x;putchar('-');}
if(x>9)write(x/10);
putchar(x%10+48);
}
int T,n;char s[10];
int val[20005],sum[20005],siz[20005],rev[20005],ch[20005][2],fa[20005];
inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
inline void pushrev(int x){swap(ch[x][0],ch[x][1]);rev[x]^=1;}
inline void pushup(int x){sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+val[x];siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;}
inline void pushdown(int x)
{
if(rev[x])
{
if(ch[x][0])pushrev(ch[x][0]);
if(ch[x][1])pushrev(ch[x][1]);
}rev[x]=0;
}
inline void update(int x)
{
if(!isroot(x))update(fa[x]);
pushdown(x);
}
inline void rotate(int x)
{
int y=fa[x],z=fa[y];
int k=ch[y][1]==x,w=ch[x][k^1];
if(!isroot(y))ch[z][ch[z][1]==y]=x;
ch[x][k^1]=y;ch[y][k]=w;
if(w){fa[w]=y;}fa[y]=x;fa[x]=z;
pushup(y);
}
inline void splay(int x)
{
update(x);
while(!isroot(x))
{
int y=fa[x],z=fa[y];
if(!isroot(y))rotate((ch[y][0]==x)^(ch[z][0]==y)?x:y);
rotate(x);
}pushup(x);
}
inline void access(int x)
{
for(int y=0;x;x=fa[y=x])
splay(x),ch[x][1]=y,pushup(x);
}
inline void makeroot(int x){access(x);splay(x);pushrev(x);}
inline void split(int x,int y){makeroot(x);access(y);splay(y);}
inline void link(int x,int y){makeroot(x);fa[x]=y;}
inline int find(int x,int k)
{
while(1)
{
pushdown(x);
if(siz[ch[x][0]]>=k)x=ch[x][0];
else if(siz[ch[x][0]]+1==k)return x;
else k-=siz[ch[x][0]]+1,x=ch[x][1];
}return 0;
}
inline void init()
{
fill(val+1,val+2*n,0);fill(sum+1,sum+2*n,0);
fill(siz+1,siz+2*n,0);fill(rev+1,rev+2*n,0);
fill(fa+1,fa+2*n,0);
for(int i=1;i<2*n;++i)ch[i][0]=ch[i][1]=0;
}
int main()
{
T=read();
while(T-->0)
{
n=read();init();
for(int i=1;i<n;++i)
{
int u=read(),v=read(),w=read();
val[n+i]=w;link(u,n+i);link(v,n+i);
}
while(1)
{
scanf("%s",s+1);if(s[2]=='O')break;
int u=read(),v=read();split(u,v);
if(s[1]=='D'){write(sum[v]),pc('\n');}
else write(find(v,2*read()-1)),pc('\n');
}
}return 0;
}
Qtree3
题目描述
给出
-
0 i
改变某点的颜色(原来是黑的变白,原来是白的变黑) -
1 v
询问 到 的路径上的第一个黑点,若无,输出
题解
接下来就不想讲树剖做法了。(只是懒)
一如既往的 LCT 水题。第一个操作直接 makeroot(x)
然后修改;第二个操作维护一个
时间复杂度
代码
#include<bits/stdc++.h>
#define pc(x) putchar(x)
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){f=ch=='-'?-1:f;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
void write(int x)
{
if(x<0){x=-x;putchar('-');}
if(x>9)write(x/10);
putchar(x%10+48);
}
int n,q;
int val[100005],sum[100005],rev[100005],ch[100005][2],fa[100005];
inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
inline void pushrev(int x){swap(ch[x][0],ch[x][1]);rev[x]^=1;}
inline void pushup(int x){sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+val[x];}
inline void pushdown(int x)
{
if(rev[x])
{
if(ch[x][0])pushrev(ch[x][0]);
if(ch[x][1])pushrev(ch[x][1]);
}rev[x]=0;
}
inline void update(int x)
{
if(!isroot(x))update(fa[x]);
pushdown(x);
}
inline void rotate(int x)
{
int y=fa[x],z=fa[y];
int k=ch[y][1]==x,w=ch[x][k^1];
if(!isroot(y))ch[z][ch[z][1]==y]=x;
ch[x][k^1]=y;ch[y][k]=w;
if(w){fa[w]=y;}fa[y]=x;fa[x]=z;
pushup(y);
}
inline void splay(int x)
{
update(x);
while(!isroot(x))
{
int y=fa[x],z=fa[y];
if(!isroot(y))rotate((ch[y][0]==x)^(ch[z][0]==y)?x:y);
rotate(x);
}pushup(x);
}
inline void access(int x)
{
for(int y=0;x;x=fa[y=x])
splay(x),ch[x][1]=y,pushup(x);
}
inline void makeroot(int x){access(x);splay(x);pushrev(x);}
inline void split(int x,int y){makeroot(x);access(y);splay(y);}
inline void link(int x,int y){makeroot(x);fa[x]=y;}
inline int find(int x)
{
if(!sum[x])return -1;
while(1)
{
pushdown(x);
if(sum[ch[x][0]])x=ch[x][0];
else if(val[x])return x;
else x=ch[x][1];
}return 0;
}
int main()
{
n=read(),q=read();
for(int i=1;i<n;++i)
{
int u=read(),v=read();
link(u,v);
}
for(int i=1;i<=q;++i)
{
int op=read(),x=read();
if(!op){makeroot(x);val[x]^=1;pushup(x);}
else split(1,x),write(find(x)),pc('\n');
}return 0;
}
Qtree4
题目描述
给出一棵边带权的节点数量为
-
C x
改变节点 的颜色,即白变黑,黑变白 -
A
询问树中最远的两个白色节点的距离,这两个白色节点可以重合(此时距离为 )。
题解
LCT做法不会,看了大佬们的题解才搞懂的。
以下部分来自原题解。
合并的过程可以手动模拟一下下,理解会更深刻。
子树最值不能直接维护,我们可以用 multiset 维护虚子树信息,单次都是
总时间复杂度是
这种做法个人其实不是很喜欢,下放边权会让代码难写难调,等什么时候想出来化边为点的做法再更新。
代码
#include<bits/stdc++.h>
#define pc(x) putchar(x)
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){f=ch=='-'?-1:f;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
void write(int x)
{
if(x<0){x=-x;putchar('-');}
if(x>9)write(x/10);
putchar(x%10+48);
}
const int inf=1e9;
int T,n,q,ans;char s[15];
struct edge{int v,w;};
vector<edge>v[100005];
int id[100005],val[100005],sum[100005],rev[100005];
int ch[100005][2],fa[100005],lmax[100005],rmax[100005],far[100005];
int h1[100005],h2[100005],p1[100005];
multiset<int>h[100005],p[100005];
void gethp(int x)
{
h1[x]=h2[x]=p1[x]=-inf;
if(h[x].size())
{
auto it=--h[x].end();h1[x]=*it;
if(h[x].size()>1)h2[x]=*--it;
}if(p[x].size())p1[x]=*--p[x].end();
}
inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
inline void pushrev(int x){swap(ch[x][0],ch[x][1]);rev[x]^=1;}
inline void pushup(int x)
{
sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+val[x];
int add=id[x]?max(0,h1[x]):h1[x];
int L=max(add,rmax[ch[x][0]]+val[x]);
int R=max(add,lmax[ch[x][1]]);
lmax[x]=max(lmax[ch[x][0]],sum[ch[x][0]]+val[x]+R);
rmax[x]=max(rmax[ch[x][1]],sum[ch[x][1]]+L);
far[x]=max(rmax[ch[x][0]]+val[x]+R,lmax[ch[x][1]]+L);
far[x]=max(far[x],max(far[ch[x][0]],far[ch[x][1]]));
far[x]=max(far[x],h1[x]+h2[x]);far[x]=max(far[x],p1[x]);
if(id[x])far[x]=max(0,max(far[x],h1[x]));
}
inline void pushdown(int x)
{
if(rev[x])
{
if(ch[x][0])pushrev(ch[x][0]);
if(ch[x][1])pushrev(ch[x][1]);
}rev[x]=0;
}
inline void update(int x)
{
if(!isroot(x))update(fa[x]);
pushdown(x);
}
inline void rotate(int x)
{
int y=fa[x],z=fa[y];
int k=ch[y][1]==x,w=ch[x][k^1];
if(!isroot(y))ch[z][ch[z][1]==y]=x;
ch[x][k^1]=y;ch[y][k]=w;
if(w){fa[w]=y;}fa[y]=x;fa[x]=z;
pushup(y);
}
inline void splay(int x)
{
update(x);
while(!isroot(x))
{
int y=fa[x],z=fa[y];
if(!isroot(y))rotate((ch[y][0]==x)^(ch[z][0]==y)?x:y);
rotate(x);
}pushup(x);
}
inline void access(int x)
{
for(int y=0;x;x=fa[y=x])
{
splay(x);
if(ch[x][1])h[x].insert(lmax[ch[x][1]]),p[x].insert(far[ch[x][1]]);
if(y)h[x].erase(h[x].find(lmax[y])),p[x].erase(p[x].find(far[y]));
gethp(x);ch[x][1]=y;pushup(x);
}
}
void dfs(int x)
{
for(edge e:v[x])
{
if(e.v==fa[x])continue;
fa[e.v]=x;val[e.v]=e.w;dfs(e.v);
h[x].insert(lmax[e.v]);p[x].insert(far[e.v]);
}gethp(x);pushup(x);
}
int main()
{
n=read();
for(int i=0;i<=n;++i)lmax[i]=rmax[i]=far[i]=-inf;
for(int i=1;i<=n;++i)id[i]=1;
for(int i=1;i<n;++i)
{
int x=read(),y=read(),w=read();
v[x].push_back((edge){y,w});
v[y].push_back((edge){x,w});
}dfs(1);ans=far[1];T=read();
while(T-->0)
{
scanf("%s",s+1);
if(s[1]=='A')
{
if(ans>=0)write(ans),pc('\n');
else puts("They have disappeared.");
}
else
{
int x=read();access(x);splay(x);
id[x]^=1;pushup(x);ans=far[x];
}
}return 0;
}
Qtree5
题目描述
你被给定一棵n个点的树,点从1到n编号。每个点可能有两种颜色:黑或白。我们定义 dist(a,b)
为点
-
0 i
将点i的颜色反转(黑变白,白变黑) -
1 v
询问dist(u,v)
的最小值。 点必须为白色( 与 可以相同),显然如果 是白点,查询得到的值一定是 。
特别地,如果询问时树上没有白点,输出
题解
************************。
这题调了一下午加四分之一个晚自习,就因为 multiset 不存在元素而删除直接 RE 了。
究极原因是 (pushup
漏写了一句话
首先 splay(v)
,
至于修改,直接 splay(v)
,然后修改颜色再 pushup(v)
就行了。
总时间复杂度
代码
#include<bits/stdc++.h>
#define pc(x) putchar(x)
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){f=ch=='-'?-1:f;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
void write(int x)
{
if(x<0){x=-x;putchar('-');}
if(x>9)write(x/10);
putchar(x%10+48);
}
const int inf=1e9;
int n,q;vector<int>e[100005];
int id[100005],dep[100005],val[100005];
int rev[100005],ch[100005][2],fa[100005],mndep[100005];
multiset<int>vir[100005];
inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
inline void pushrev(int x){swap(ch[x][0],ch[x][1]);rev[x]^=1;}
inline void pushup(int x)
{
mndep[x]=min(id[x]?dep[x]:inf,vir[x].size()?(*vir[x].begin()):inf);
if(mndep[x]!=inf)val[x]=mndep[x]-2*dep[x]; else val[x]=inf;
if(ch[x][0])val[x]=min(val[x],val[ch[x][0]]),mndep[x]=min(mndep[x],mndep[ch[x][0]]);
if(ch[x][1])val[x]=min(val[x],val[ch[x][1]]),mndep[x]=min(mndep[x],mndep[ch[x][1]]);
}
inline void pushdown(int x)
{
if(rev[x])
{
if(ch[x][0])pushrev(ch[x][0]);
if(ch[x][1])pushrev(ch[x][1]);
}rev[x]=0;
}
inline void update(int x)
{
if(!isroot(x))update(fa[x]);
pushdown(x);
}
inline void rotate(int x)
{
int y=fa[x],z=fa[y];
int k=ch[y][1]==x,w=ch[x][k^1];
if(!isroot(y))ch[z][ch[z][1]==y]=x;
ch[x][k^1]=y;ch[y][k]=w;
if(w){fa[w]=y;}fa[y]=x;fa[x]=z;
pushup(y);
}
inline void splay(int x)
{
update(x);
while(!isroot(x))
{
int y=fa[x],z=fa[y];
if(!isroot(y))rotate((ch[y][0]==x)^(ch[z][0]==y)?x:y);
rotate(x);
}pushup(x);
}
inline void access(int x)
{
for(int y=0;x;x=fa[y=x])
{
splay(x);
if(ch[x][1])vir[x].insert(mndep[ch[x][1]]);
if(y)vir[x].erase(vir[x].find(mndep[y]));
ch[x][1]=y,pushup(x);
}
}
void dfs(int x)
{
for(int y:e[x])
{
if(y==fa[x])continue;
fa[y]=x;dep[y]=dep[x]+1;
dfs(y);vir[x].insert(mndep[y]);
}pushup(x);
}
int main()
{
n=read();
for(int i=1;i<n;++i)
{
int u=read(),v=read();
e[u].push_back(v);
e[v].push_back(u);
}dep[1]=1;dfs(1);q=read();
for(int i=1;i<=q;++i)
{
int op=read(),x=read();
if(!op){access(x);splay(x);id[x]^=1;pushup(x);}
else{access(x);splay(x);write(val[x]==inf?-1:val[x]+dep[x]),pc('\n');}
}return 0;
}
Qtree6
题目描述
给你一棵
0 u
询问有多少个节点 满足路径 到 上所有节点(包括 )都拥有相同的颜色。1 u
翻转 的颜色。
题解
连通块问题,用两个 LCT 分别维护同色连通块,再维护虚子树大小即可。问题在于修改,更改一个点的颜色就要断掉周围的所有边,如果是一个菊花图就卡掉了。
实际上我们只需要断掉当前颜色的父边,连上另一种颜色的父边,此时这个点是当前颜色子树的根,我们可以把它看作一个虚点,统计答案时不用管它,直接统计儿子的信息;至于另一种颜色,因为我们连上了父边,所以统计答案时会统计到自己。这样每次操作的复杂度是正确的
代码
#include<bits/stdc++.h>
#define pc(x) putchar(x)
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){f=ch=='-'?-1:f;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
void write(int x)
{
if(x<0){x=-x;putchar('-');}
if(x>9)write(x/10);
putchar(x%10+48);
}
int n,m;
vector<int>e[100005];
int col[100005],f[100005];
struct LCT
{
int fa[100005],siz[100005],vir[100005],ch[100005][2];
inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
inline void pushup(int x){siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+vir[x]+1;}
inline void rotate(int x)
{
int y=fa[x],z=fa[y];
int k=ch[y][1]==x,w=ch[x][k^1];
if(!isroot(y))ch[z][ch[z][1]==y]=x;
ch[x][k^1]=y;ch[y][k]=w;
if(w){fa[w]=y;}fa[y]=x;fa[x]=z;
pushup(y);
}
inline void splay(int x)
{
while(!isroot(x))
{
int y=fa[x],z=fa[y];
if(!isroot(y))rotate((ch[y][0]==x)^(ch[z][0]==y)?x:y);
rotate(x);
}pushup(x);
}
inline void access(int x)
{
for(int y=0;x;x=fa[y=x])
{
splay(x);vir[x]+=siz[ch[x][1]];
vir[x]-=siz[y];ch[x][1]=y;pushup(x);
}
}
inline int findroot(int x)
{
access(x);splay(x);
while(ch[x][0])x=ch[x][0];
splay(x);return x;
}
inline void link(int x)
{
splay(x);int y=fa[x]=f[x];
access(y);splay(y);vir[y]+=siz[x];
pushup(y);
}
inline void cut(int x)
{
access(x);splay(x);
ch[x][0]=fa[ch[x][0]]=0;
pushup(x);
}
}tr[2];
void dfs(int x)
{
for(int y:e[x])if(y!=f[x])
f[y]=x,dfs(y),tr[0].link(y);
}
int main()
{
n=read();
for(int i=1;i<n;++i)
{
int u=read(),v=read();
e[u].push_back(v);
e[v].push_back(u);
}dfs(1);f[1]=n+1;tr[0].link(1);m=read();
for(int i=1;i<=m;++i)
{
int op=read(),x=read();
if(op)tr[col[x]].cut(x),tr[col[x]^=1].link(x);
else{int y=tr[col[x]].findroot(x);write(tr[col[x]].siz[tr[col[x]].ch[y][1]]),pc('\n');}
}return 0;
}
Qtree7
题目描述
一棵树,每个点初始有个点权和颜色。有三种操作:
-
0 u
询问所有 路径上的最大点权,要满足 路径上所有点的颜色都相同。 -
1 u
反转 的颜色。 -
2 u w
把 的点权改成 。
题解
会了 Qtree6,这题就很水了。和上一题一样,要维护同色连通块以及虚子树最大值。这个就相当于结合了前面几个题目的做法,维护同色连通块就开
代码
#include<bits/stdc++.h>
#define pc(x) putchar(x)
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){f=ch=='-'?-1:f;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
void write(int x)
{
if(x<0){x=-x;putchar('-');}
if(x>9)write(x/10);
putchar(x%10+48);
}
const int inf=1e9;
int n,m;
vector<int>e[100005];
int col[100005],f[100005];
int val[100005];
struct LCT
{
int fa[100005],mx[100005],ch[100005][2];
multiset<int>vir[100005];LCT(){mx[0]=-inf;}
inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
inline void pushup(int x)
{
mx[x]=val[x];if(vir[x].size())mx[x]=max(mx[x],*vir[x].rbegin());
mx[x]=max(mx[x],max(mx[ch[x][0]],mx[ch[x][1]]));
}
inline void rotate(int x)
{
int y=fa[x],z=fa[y];
int k=ch[y][1]==x,w=ch[x][k^1];
if(!isroot(y))ch[z][ch[z][1]==y]=x;
ch[x][k^1]=y;ch[y][k]=w;
if(w){fa[w]=y;}fa[y]=x;fa[x]=z;
pushup(y);
}
inline void splay(int x)
{
while(!isroot(x))
{
int y=fa[x],z=fa[y];
if(!isroot(y))rotate((ch[y][0]==x)^(ch[z][0]==y)?x:y);
rotate(x);
}pushup(x);
}
inline void access(int x)
{
for(int y=0;x;x=fa[y=x])
{
splay(x);vir[x].insert(mx[ch[x][1]]);
vir[x].erase(vir[x].find(mx[y]));
ch[x][1]=y;pushup(x);
}
}
inline int findroot(int x)
{
access(x);splay(x);
while(ch[x][0])x=ch[x][0];
splay(x);return x;
}
inline void link(int x)
{
splay(x);int y=fa[x]=f[x];
access(y);splay(y);
vir[y].insert(mx[x]);
pushup(y);
}
inline void cut(int x)
{
access(x);splay(x);
ch[x][0]=fa[ch[x][0]]=0;
pushup(x);
}
inline void modify(int x,int w)
{
access(x);splay(x);
val[x]=w;pushup(x);
}
}tr[2];
void dfs(int x)
{
for(int y:e[x])if(y!=f[x])
f[y]=x,dfs(y),tr[0].link(y);
}
int main()
{
n=read();
for(int i=1;i<n;++i)
{
int u=read(),v=read();
e[u].push_back(v);
e[v].push_back(u);
}
for(int i=1;i<=n;++i)col[i]=read();
for(int i=1;i<=n;++i)val[i]=read();
dfs(1);f[1]=n+1;tr[0].link(1);m=read();
for(int i=1;i<=n;++i)if(col[i])tr[0].cut(i),tr[1].link(i);
for(int i=1;i<=m;++i)
{
int op=read(),x=read();
if(op==1)tr[col[x]].cut(x),tr[col[x]^=1].link(x);
else if(op==2){tr[col[x]].modify(x,read());}
else{int y=tr[col[x]].findroot(x);write(tr[col[x]].mx[tr[col[x]].ch[y][1]]),pc('\n');}
}return 0;
}
后记
这几道题做了好几天(中途鸽了好久),做种题没见过就真不会,做过一遍这一类型就都掌握了。做的意义还是蛮大的,对 LCT 处理树上问题能更好的思考和理解。
完结撒花~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】