题意
给定一棵有根树,有三种操作:换根,查询某个子树点权最小值,将一条路径上的点重新赋值
对于100%的数据,n<=100000,m<=100000,0<=所有权值<231。
题解
如果不换根的话,就是一道树链剖分的简单题;
那么换根要如何操作?LCT?
考虑换根会带来什么影响,首先换根不会改变两个点之间的路径,书上两点的路径是固定的。
那对于子树呢?
拿这样一棵树做例子,假设当前根root=4
那么这些点的子树会改变:它本身的子树变成整棵树,在原树上在他和最初根节点的路径上的点(1,2);其余的不变;
考虑第二种怎么改变,可以看得出他的子树变成了整棵树减去一棵子树,减去的子树就是他的包含root的儿子;
也正是这个性质,可以想到在求最小值的时候在dfs序上被减去的子树分成了两段区间,这样这个问题就可解了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; #define ll long long const int maxn=100005; const ll oo=210000000000; int n,m,nowroot; int a[maxn],aa[maxn]; int cnt,head[maxn]; int dep[maxn],fa[maxn][25],size[maxn],son[maxn]; int top[maxn],id[maxn]; int root,ls[maxn<<1],rs[maxn<<1]; ll mi[maxn<<1],tag[maxn<<1]; struct edge{ int y,next; }e[maxn<<1]; template<class T>void read(T &x){ x=0;char ch=getchar(); while(!isdigit(ch)) ch=getchar(); while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} } ll min(ll x,ll y){return x<y ? x : y ;} void add(int x,int y){ e[++cnt]=(edge){y,head[x]}; head[x]=cnt; } void dfs(int u){ size[u]=1; for(int i=1;i<=20;i++) fa[u][i]=fa[fa[u][i-1]][i-1]; for(int i=head[u];i;i=e[i].next){ int v=e[i].y; if(v==fa[u][0]) continue; fa[v][0]=u; dep[v]=dep[u]+1; dfs(v); size[u]+=size[v]; if(size[son[u]]<size[v]) son[u]=v; } } void dfs(int u,int tp){ id[u]=++cnt; aa[cnt]=a[u]; top[u]=tp; if(!son[u]) return ; dfs(son[u],tp); for(int i=head[u];i;i=e[i].next){ int v=e[i].y; if(v==fa[u][0]||v==son[u]) continue; dfs(v,v); } } void update(int rt){ mi[rt]=min(mi[ls[rt]],mi[rs[rt]]); } void build(int &rt,int l,int r){ rt=++cnt;tag[rt]=-1; if(l==r) {mi[rt]=aa[l];return ;} int mid=(l+r)>>1; build(ls[rt],l,mid); build(rs[rt],mid+1,r); update(rt); } void put_tag(int rt,ll val){ mi[rt]=tag[rt]=val; } void push_down(int rt){ put_tag(ls[rt],tag[rt]); put_tag(rs[rt],tag[rt]); tag[rt]=-1; } void modify(int rt,int l,int r,int a_l,int a_r,ll val){ if(a_l<=l&&r<=a_r){ put_tag(rt,val); return ; } int mid=(l+r)>>1; if(tag[rt]!=-1) push_down(rt); if(a_l<=mid) modify(ls[rt],l,mid,a_l,a_r,val); if(mid<a_r) modify(rs[rt],mid+1,r,a_l,a_r,val); update(rt); } void modify(int x,int y,ll val){ while(top[x]!=top[y]){ if(dep[top[x]]>dep[top[y]]) swap(x,y); modify(1,1,n,id[top[y]],id[y],val); y=fa[top[y]][0]; } if(dep[x]>dep[y]) swap(x,y); modify(1,1,n,id[x],id[y],val); } ll query(int rt,int l,int r,int a_l,int a_r){ if(a_l<=l&&r<=a_r) return mi[rt]; if(tag[rt]!=-1) push_down(rt); int mid=(l+r)>>1; ll ans=oo; if(a_l<=mid) ans=min(ans,query(ls[rt],l,mid,a_l,a_r)); if(mid<a_r) ans=min(ans,query(rs[rt],mid+1,r,a_l,a_r)); return ans; } int main(){ read(n);read(m); for(int i=1;i<n;i++){ int x,y; read(x);read(y); add(x,y);add(y,x); } for(int i=1;i<=n;i++) read(a[i]); read(root); dep[root]=1; dfs(root); cnt=0; dfs(root,root); cnt=0; build(ls[0],1,n); for(int i=1;i<=m;i++){ int op;read(op); if(op==1) read(root); else if(op==2){ int x,y; ll z; read(x);read(y);read(z); modify(x,y,z); } else { int x;read(x); if(x==root) printf("%lld ",mi[1]); //整颗树 else if(id[root]>id[x]&&id[root]<id[x]+size[x]){//x的子树改变 int pos=root,delt=dep[pos]-dep[x]-1; for(int p=0;delt;delt>>=1,p++)//倍增 if(delt&1) pos=fa[pos][p]; printf("%lld ",min(query(1,1,n,1,id[pos]-1),query(1,1,n,id[pos]+size[pos],n))); } else printf("%lld ",query(1,1,n,id[x],id[x]+size[x]-1));//无影响 } } }