题解 P3979 【遥远的国度】

· · 题解

\text{前言} $\quad$另外洛谷上还有一道关于换根操作的题目:[CF916E Jamie and Tree](https://www.luogu.com.cn/problem/CF916E)([我的题解](https://www.luogu.com.cn/blog/Farkas/solution-cf916e)) $$\text{关于题目要求的操作}$$ 1. 换根,直接换即可 2. 路径修改,就和普通树剖一样。 3. 子树修改,这个需要分类讨论。(下面会细讲) ![](https://cdn.luogu.com.cn/upload/image_hosting/gmu2iblr.png)) $$\text{换根}$$ $\quad$因为每换一次根,树中的很多信息都会改变,不可能每次换根都跑两便 $dfs$ 预处理,所以我们考虑其他方法,对于单纯的换根操作,只需要设置一个全局变量 $root$ 来存储根的编号( $root$ 初始化为 $1$ ,默认以 $1$ 为根),对于其他操作,再通过分类讨论 $root$ 的位置来进行操作。 $$\text{子树修改(查询)}$$ ![](https://cdn.luogu.com.cn/upload/image_hosting/4ar4m3w5.png) $\quad$ 情况 $1$ :当 $x=root$ 时, $x$ 就是此时整棵树的根,那么就是全局修改(查询)。 $\quad$ 情况 $2$ :当 $root$ 在x子树中时,就需要特别判断了,根据图像我们可以发现此时x的真正子树是包括除了 $root$ 方向上的子树之外其他所有节点。 $\quad$ 对于求最小值,需要去除root这颗子树上的范围,所以我打了一个特别的线段树查询,另外还需要一个find操作来寻找x中root所在的儿子节点。 ```cpp il int query2(int k,int l,int r,int x,int y) { if(l>y||r<x)return sum[k]; if(x<=l&&y>=r)return inf; int mid=l+r>>1,res=inf; if(c[k])pushdown(k,l,r,mid); res=min(res,query2(k<<1,l,mid,x,y)); res=min(res,query2(k<<1|1,mid+1,r,x,y)); return res; } il int find(int x,int y) { int fx=top[x],fy=top[y]; while(fx!=fy) { if(dep[fx]<dep[fy])swap(x,y),swap(fx,fy); if(father[fx]==y)return top[x]; x=father[fx];fx=top[x]; } if(dep[x]>dep[y])swap(x,y); return son[x]; } il int query1(int x) { int res=0; if(x==root){return query(1,1,n,1,n);} if(seg[root]>=seg[x]&&seg[root]<=seg[x]+size[x]-1){//判断root在x的子树中 int y=find(x,root); res=query2(1,1,n,seg[y],seg[y]+size[y]-1); return res; } return query(1,1,n,seg[x],seg[x]+size[x]-1); } ``` $\quad$ 情况 $3$ :其他情况下 $x$ 的子树以 $root$ 为根和以 $1$ 为根是一样的。 $$\text{完整代码}$$ ```cpp #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<stack> using namespace std; #define int long long #define next neee #define re register int #define il inline #define inf 1e18 il int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)&&ch!='-')ch=getchar(); if(ch=='-')f=-1,ch=getchar(); while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); return x*f;} il void print(int x) { if(x<0)putchar('-'),x=-x; if(x/10)print(x/10); putchar(x%10+'0');} const int N=1e6+5; int n,m,next[N<<1],go[N<<1],head[N],tot,a[N],top[N],root; int sum[N<<2],seg[N],rev[N],son[N],size[N],dep[N],father[N],c[N<<2]; il void Add(int x,int y) {next[++tot]=head[x];head[x]=tot;go[tot]=y;} il void dfs1(int x,int fa) { father[x]=fa;dep[x]=dep[fa]+1;size[x]=1; for(re i=head[x],y;i,y=go[i];i=next[i]) { if(y==fa)continue; dfs1(y,x); size[x]+=size[y]; if(size[y]>size[son[x]])son[x]=y; } } il void dfs2(int x,int topf) { top[x]=topf;seg[x]=++seg[0];rev[seg[x]]=x; if(!son[x])return; dfs2(son[x],topf); for(re i=head[x],y;i,y=go[i];i=next[i]) { if(top[y])continue; dfs2(y,y); } } il void build(int k,int l,int r)//建树 { if(l==r){sum[k]=a[rev[l]];return;} int mid=l+r>>1; build(k<<1,l,mid);build(k<<1|1,mid+1,r); sum[k]=min(sum[k<<1],sum[k<<1|1]); } il void ADD(int k,int l,int r,int v){sum[k]=c[k]=v;}//修改 il void pushdown(int k,int l,int r,int mid)//下传懒标记 { if(l==r){c[k]=0;return;} ADD(k<<1,l,mid,c[k]);ADD(k<<1|1,mid+1,r,c[k]); c[k]=0;} il void change1(int k,int l,int r,int x,int y,int z)//区间修改 { if(x<=l&&y>=r){ADD(k,l,r,z);return;} int mid=l+r>>1; if(c[k])pushdown(k,l,r,mid); if(x<=mid)change1(k<<1,l,mid,x,y,z); if(y>mid)change1(k<<1|1,mid+1,r,x,y,z); sum[k]=min(sum[k<<1],sum[k<<1|1]); } il int query(int k,int l,int r,int x,int y)//区间询问 { if(x<=l&&y>=r)return sum[k]; int mid=l+r>>1,res=inf; if(c[k])pushdown(k,l,r,mid); if(x<=mid)res=query(k<<1,l,mid,x,y); if(y>mid)res=min(res,query(k<<1|1,mid+1,r,x,y)); return res; } il int query2(int k,int l,int r,int x,int y)//特殊的区间询问 { if(l>y||r<x)return sum[k]; if(x<=l&&y>=r)return inf; int mid=l+r>>1,res=inf; if(c[k])pushdown(k,l,r,mid); res=min(res,query2(k<<1,l,mid,x,y)); res=min(res,query2(k<<1|1,mid+1,r,x,y)); return res; } il int find(int x,int y)//找root所在的儿子 { int fx=top[x],fy=top[y]; while(fx!=fy) { if(dep[fx]<dep[fy])swap(x,y),swap(fx,fy); if(father[fx]==y)return top[x]; x=father[fx];fx=top[x]; } if(dep[x]>dep[y])swap(x,y); return son[x]; } il void change2(int x,int y,int z)//路径修改 { int fx=top[x],fy=top[y]; while(fx!=fy) { if(dep[fx]<dep[fy])swap(x,y),swap(fx,fy); change1(1,1,n,seg[fx],seg[x],z); x=father[fx];fx=top[x]; } if(dep[x]>dep[y])swap(x,y); change1(1,1,n,seg[x],seg[y],z); } il int query1(int x)//子树修改 { int res=0; if(x==root){return query(1,1,n,1,n);} if(seg[root]>=seg[x]&&seg[root]<=seg[x]+size[x]-1){ int y=find(x,root); res=query2(1,1,n,seg[y],seg[y]+size[y]-1); return res; } return query(1,1,n,seg[x],seg[x]+size[x]-1); } signed main() { n=read();m=read(); for(re i=1;i<n;i++){re x=read(),y=read();Add(x,y);Add(y,x);} for(re i=1;i<=n;i++)a[i]=read(); root=read();dfs1(root,0);dfs2(root,root);build(1,1,n); while(m--) { re k=read(); if(k==1)root=read(); if(k==2){re x=read(),y=read(),z=read();change2(x,y,z);} if(k==3){re x=read();print(query1(x));putchar('\n');} } return 0; } ``` $\quad$ 以上就是全部内容了,写题解不易,不妨点个赞吧!