P397遥远的国度(树剖换根)
分析
树剖+线段树+分类讨论(树剖换根)
我们先在这里放结论
- 换根,不影响我们用从1建立的树剖跑树中路径的操作。
- 换根,会影响利用子树的性质,因为换过根后,树中结点子树的节点
dfs序可能就不连续了,但可以通过分类讨论避免掉,具体看下文。
我们来以本题作为范本讲讲树剖换根
总共三个操作,我们需要关注的仅仅只有第三个操作。
因为根会换,因此,对于同一个节点,当根不同时,其子树也不一定相同。显然,每换一次根就跑一次树剖是不理智的。
那就到了我们需要讲述的关键了,我们可以进行分类讨论
注意:我们这里只根据一号点进行了树剖,root是题目中的首都,在我们的树剖中,我们用的是一号点作为根
- 需要查询的点
u即为root,则直接查询整棵树即可 - 需要查询的点
u不在1->root的路径中,那就直接查询该节点下的子树即可 - 也是重头戏,需要查询的点
u在1=>root的路径中,我们只需要删掉从u->root的路径中u的直接儿子v的子树这一部分。而这一部分是连续的,我们只需要查这一部分的两边的连续区间即可得到答案。
主要就是第3种,也很好想
找到路径u->root上的u的直系儿子v
就会发现root为根时,u子树覆盖不到的地方是v及v的子树
下边说一下,怎么找到直系儿子v
就跟树剖找LCA相同,只是最后的时候需要特判一下。
若是最后的时候,我们从一条链翻上去的时候,直接就到了u,那直接返回该条链的头即可
若是已经翻到了同一条链,则直接返回son[u]即可。
int u = root;
while(top[u]!=top[x])
{
if(fa[top[u]]==x) return top[u];
u = fa[top[u]];
}
return son[x];
下边直接看代码
Ac_code
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10,INF = INT_MAX;
struct Node
{
int l,r,mi,tag;
}tr[N<<2];
int h[N],e[N<<1],ne[N<<1],w[N],idx;
int sz[N],fa[N],son[N],dep[N];
int top[N],id[N],nw[N],ts;
int n,m,root;
void add(int a,int b)
{
e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
void dfs1(int u,int pa,int depth)
{
sz[u] = 1,fa[u] = pa,dep[u] = depth;
for(int i=h[u];~i;i=ne[i])
{
int j = e[i];
if(j==pa) continue;
dfs1(j,u,depth+1);
sz[u] += sz[j];
if(sz[j]>sz[son[u]]) son[u] = j;
}
}
void dfs2(int u,int tp)
{
top[u] = tp,id[u] = ++ts,nw[ts] = w[u];
if(!son[u]) return ;
dfs2(son[u],tp);
for(int i=h[u];~i;i=ne[i])
{
int j = e[i];
if(j==fa[u]||j==son[u]) continue;
dfs2(j,j);
}
}
void pushup(int u)
{
tr[u].mi = min(tr[u<<1].mi,tr[u<<1|1].mi);
}
void pushdown(int u)
{
auto &root = tr[u],&left = tr[u<<1],&right = tr[u<<1|1];
if(root.tag)
{
left.mi = right.mi = left.tag = right.tag = root.tag;
root.tag = 0;
}
}
void build(int u,int l,int r)
{
if(l==r)
{
tr[u] = {l,r,nw[l]};
return ;
}
tr[u] = {l,r};
int mid = l + r >> 1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
pushup(u);
}
void modify(int u,int l,int r,int x)
{
if(l<=tr[u].l&&tr[u].r<=r)
{
tr[u].mi = tr[u].tag = x;
return ;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l<=mid) modify(u<<1,l,r,x);
if(r>mid) modify(u<<1|1,l,r,x);
pushup(u);
}
int query(int u,int l,int r)
{
if(l<=tr[u].l&&tr[u].r<=r) return tr[u].mi;
int mid = tr[u].l + tr[u].r >> 1;
pushdown(u);
int res = INF;
if(l<=mid) res = min(res,query(u<<1,l,r));
if(r>mid) res = min(res,query(u<<1|1,l,r));
return res;
}
int find(int x)
{
if(x==root) return 1;
if(id[x]>=id[root]||id[x]+sz[x]-1<id[root]) return x;
int u = root;
while(top[u]!=top[x])
{
if(fa[top[u]]==x) return top[u];
u = fa[top[u]];
}
return son[x];
}
int main()
{
scanf("%d%d",&n,&m);
memset(h,-1,sizeof h);
for(int i=0;i<n-1;i++)
{
int u,v;scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
for(int i=1;i<=n;i++) scanf("%d",w+i);
scanf("%d",&root);
dfs1(1,-1,1);
dfs2(1,1);
build(1,1,n);
while(m--)
{
int op,x,y,z;
scanf("%d%d",&op,&x);
if(op==1) root = x;
else if(op==2)
{
scanf("%d%d",&y,&z);
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
modify(1,id[top[x]],id[x],z);
x = fa[top[x]];
}
if(dep[x]<dep[y]) swap(x,y);
modify(1,id[y],id[x],z);
}
else
{
int u = find(x);
if(u==1) printf("%d\n",query(1,1,n));
else if(u==x) printf("%d\n",query(1,id[u],id[u]+sz[u]-1));
else
{
int res = query(1,1,id[u]-1);
if(id[u]+sz[u]<=n) res = min(res,query(1,id[u]+sz[u],n));
printf("%d\n",res);
}
}
}
return 0;
}

浙公网安备 33010602011771号