题面:传送门
链上修改,子树查询,一看就是树链剖分,链修改不需要说了吧,查询 x x 的子树只需要查询即可。
最难的也是最最关键的地方就是换根操作了。稍稍想一想就知道,不可能是真的换根,所以,我们要将换根操作转化成别的东西。
画图模拟一下,就会发现在当前根为 root r o o t ,查询的子树的根为 x x 的时候,有3种不同的情况:
①若,直接查询整棵树即可。
②若在一开始的树中, x x 为的祖先,答案为除了包含 root r o o t 的 x x 的子树以外的所有点的最小值。
③否则,直接查询原树中的子树。
这道题就这样做完了。
时间复杂度: Θ(nlog22n) Θ ( n l o g 2 2 n ) 。
全部代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<vector>
using namespace std;
int val[100010];
int pos[100010];
int siz[100010];
int mymin(int x,int y)
{
if(x==-1)return y;
if(y==-1)return x;
return min(x,y);
}
namespace sgt
{
int mn[300010],root,cnt,son[300010][2],lazy[300010];
void pushup(int x)
{
mn[x]=min(mn[son[x][0]],mn[son[x][1]]);
}
void pushdown(int x)
{
if(lazy[x])
{
mn[x]=lazy[x];
if(son[x][0] && son[x][1])lazy[son[x][0]]=lazy[son[x][1]]=lazy[x];
lazy[x]=0;
}
}
void build(int &x,int l,int r)
{
x=++cnt;
if(l==r)
{
mn[x]=val[l];
return;
}
int mid=(l+r)>>1;
build(son[x][0],l,mid);
build(son[x][1],mid+1,r);
pushup(x);
}
void update(int a,int b,int k,int l,int r,int v)
{
pushdown(k);
if(a>r || b<l)return;
if(a<=l && b>=r)
{
lazy[k]=v;
pushdown(k);
}
else
{
int mid=(l+r)>>1;
update(a,b,son[k][0],l,mid,v);
update(a,b,son[k][1],mid+1,r,v);
pushup(k);
}
}
int query(int a,int b,int k,int l,int r)
{
pushdown(k);
if(a>r || b<l)return -1;
if(a<=l && b>=r)return mn[k];
int mid=(l+r)>>1;
int res=mymin(query(a,b,son[k][0],l,mid),query(a,b,son[k][1],mid+1,r));
pushup(k);
return res;
}
}
int n,m;
vector<int>G[100010],son[100010];
int d[100010];
int par[100010][21];
int dep[100010];
int sps[100010];
void dfs(int x,int p)
{
siz[x]=1;
for(int i=0;i<G[x].size();i++)
{
int y=G[x][i];
if(y==p)continue;
son[x].push_back(y);
par[y][0]=x;
dep[y]=dep[x]+1;
dfs(y,x);
siz[x]+=siz[y];
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
for(int i=20;i>=0;i--)if(dep[par[x][i]]>=dep[y])x=par[x][i];
if(x==y)return x;
for(int i=20;i>=0;i--)if(par[x][i]!=par[y][i])x=par[x][i],y=par[y][i];
return par[x][0];
}
int rt[100010];
int root;
int ind;
void HLD(int x,int lst)
{
rt[x]=lst;
pos[x]=++ind;
val[ind]=d[x];
if(sps[x])HLD(sps[x],lst);
for(int i=0;i<son[x].size();i++)if(son[x][i]!=sps[x])HLD(son[x][i],son[x][i]);
}
int gtka(int x,int k)
{
for(int i=20;i>=0;i--)
{
if((1<<i)<=k)
{
k-=(1<<i);
x=par[x][i];
}
}
return x;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}
for(int i=1;i<=n;i++)scanf("%d",d+i);
scanf("%d",&root);
dep[0]=-1;
dfs(1,0);
for(int i=1;i<=20;i++)
{
for(int j=1;j<=n;j++)par[j][i]=par[par[j][i-1]][i-1];
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<son[i].size();j++)
{
if(!sps[i] || siz[son[i][j]]>siz[sps[i]])sps[i]=son[i][j];
}
}
HLD(1,1);
sgt::build(sgt::root,1,n);
int cur=0;
while(m--)
{
int op;
scanf("%d",&op);
if(op==1)
{
int x;
scanf("%d",&x);
root=x;
}
else if(op==2)
{
int x,y,v;
scanf("%d%d%d",&x,&y,&v);
int xy=lca(x,y);
while(rt[x]!=rt[xy])
{
sgt::update(pos[rt[x]],pos[x],sgt::root,1,n,v);
x=par[rt[x]][0];
}
sgt::update(pos[xy],pos[x],sgt::root,1,n,v);
while(rt[y]!=rt[xy])
{
sgt::update(pos[rt[y]],pos[y],sgt::root,1,n,v);
y=par[rt[y]][0];
}
sgt::update(pos[xy],pos[y],sgt::root,1,n,v);
}
else
{
cur++;
int x;
scanf("%d",&x);
if(x==root)printf("%d\n",sgt::query(1,n,sgt::root,1,n));
else if(pos[root]>=pos[x] && pos[root]<=pos[x]+siz[x]-1)
{
int t=gtka(root,dep[root]-dep[x]-1);
printf("%d\n",mymin(sgt::query(1,pos[t]-1,sgt::root,1,n),sgt::query(pos[t]+siz[t],n,sgt::root,1,n)));
}
else printf("%d\n",sgt::query(pos[x],pos[x]+siz[x]-1,sgt::root,1,n));
}
}
return 0;
}