题解 P3979 【遥远的国度】
\[\text{前言}
\]
\(\quad\)可以看看我的一篇blog关于树链剖分"换根操作"笔记(内容都差不多)
\(\quad\)另外洛谷上还有一道关于换根操作的题目:CF916E Jamie and Tree(我的题解)
\[\text{关于题目要求的操作}
\]
-
换根,直接换即可
-
路径修改,就和普通树剖一样。
-
子树修改,这个需要分类讨论。(下面会细讲)
)
\[\text{换根}
\]
\(\quad\)因为每换一次根,树中的很多信息都会改变,不可能每次换根都跑两便 \(dfs\) 预处理,所以我们考虑其他方法,对于单纯的换根操作,只需要设置一个全局变量 \(root\) 来存储根的编号( \(root\) 初始化为 \(1\) ,默认以 \(1\) 为根),对于其他操作,再通过分类讨论 \(root\) 的位置来进行操作。
\[\text{子树修改(查询)}
\]
\(\quad\) 情况 \(1\) :当 \(x=root\) 时, \(x\) 就是此时整棵树的根,那么就是全局修改(查询)。
\(\quad\) 情况 \(2\) :当 \(root\) 在x子树中时,就需要特别判断了,根据图像我们可以发现此时x的真正子树是包括除了 \(root\) 方向上的子树之外其他所有节点。
\(\quad\) 对于求最小值,需要去除root这颗子树上的范围,所以我打了一个特别的线段树查询,另外还需要一个find操作来寻找x中root所在的儿子节点。
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{完整代码}
\]
#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\) 以上就是全部内容了,写题解不易,不妨点个赞吧!