bzoj3083遥远的国度——树链剖分处理换根操作时的分类讨论

题目:bzoj3083.

没有账号的可以去水洛谷(我也没有账号):luogu3979.

题目大意:维护一个数据结构,在树上支持:

1.格式1 x,表示将根换成x.

2.格式2 x y z,表示将x到y的路径上的节点的权值都修改为z.

3.格式3 x,表示询问以x为根的子树的点权最小值.

那么这道题我们假设没有换根这种操作,就会十分简单,一个裸裸的树剖.

但是有换根操作怎么办?

我们可以很容易的得出一个结论,根是哪一个跟链的关系并不大.

但是子树呢?

我们可以画一张图看看(节点上标的数字不是编号,是权值):

扫描二维码关注公众号,回复: 2204590 查看本文章


图画的丑不要介意.

这张图中,我们可以发现,只有原根和新根之间的这条链的子树发生了改变,而别的节点的子树都没有发生改变.

所以我们可以分类讨论,若不是在这条链之间的节点,我们直接返回子树的最小值.

若是这条链上的节点,则分成两类讨论:

1.若当前根是询问节点,直接返回这棵树的最小值.

2.若询问节点是当前根的祖先,可以发现,本来在这条链上父亲-儿子的关系发生了逆转变成了儿子-父亲的关系,也就是说现在这个点要查询的最小值范围应该是整棵树去掉这个儿子子树的那部分.

那么这个儿子的范围该怎么求?

我们可以确定这条链,那按照这条链不断往上跳,直到跳到的点的父亲是当前这个点.

但是这样就会TLE了.

一个可行的方案是记录一个倍增数组,然后直接想倍增LCA的方式找到这个儿子.

另一个问题是,怎么判断x是否在这条链上?

我们可以肯定的一点是,若x和当前根的LCA是x的话,那么x肯定在这条链上.

那么这道题就很简单了:

#include<bits/stdc++.h>
  using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=100000;
const int C=20;
const int INF=2147213647;
struct side{
 int y,next;
}e[N*2+9];
struct tree{
  int l,r,min,tag;
}tr[N*5+9];
struct node{
  int deep,dfn,size,son,top,dad,v,grand[C+1],low;
}nod[N+9];
int now,n,m,lin[N+9],top,t,order[N+9];
Abigail ins(int X,int Y){
  e[++top].y=Y;
  e[top].next=lin[X];
  lin[X]=top;
}
void dfs1(int k,int fa){
  nod[k].dad=fa;
  nod[k].deep=nod[fa].deep+1;
  nod[k].grand[0]=fa;
  nod[k].size=1;
  for (int i=1;i<=C&&nod[k].grand[i-1];i++)
    nod[k].grand[i]=nod[nod[k].grand[i-1]].grand[i-1];
  for (int i=lin[k];i;i=e[i].next)
    if (fa^e[i].y){
      dfs1(e[i].y,k);
      nod[k].size+=nod[e[i].y].size;
      if (nod[nod[k].son].size<nod[e[i].y].size) nod[k].son=e[i].y;
    }
}
void dfs2(int k,int start){
  nod[k].dfn=++t;
  order[t]=k;
  nod[k].top=start;
  if (nod[k].son) dfs2(nod[k].son,start);
  for (int i=lin[k];i;i=e[i].next)
    if (e[i].y^nod[k].dad&&e[i].y^nod[k].son) dfs2(e[i].y,e[i].y);
  nod[k].low=t;
}
void pushup(int k){
  tr[k].min=min(tr[k<<1].min,tr[k<<1|1].min);
}
void pushdown(int k){
  if (!tr[k].tag) return;
  int ls=k<<1,rs=k<<1|1;
  tr[ls].tag=tr[rs].tag=tr[ls].min=tr[rs].min=tr[k].tag;
  tr[k].tag=0;
}
void build(int L,int R,int k=1){
  tr[k].l=L;tr[k].r=R;
  if (L==R){
    tr[k].min=nod[order[L]].v;
    return;
  }
  int mid=L+R>>1;
  build(L,mid,k<<1),build(mid+1,R,k<<1|1);
  pushup(k);
}
void change(int L,int R,int num,int k=1){
  if (L==tr[k].l&&R==tr[k].r){
    tr[k].min=num;
    tr[k].tag=num;
    return;
  }
  pushdown(k);
  int mid=tr[k].l+tr[k].r>>1;
  if (R<=mid) change(L,R,num,k<<1);
  else if (L>mid) change(L,R,num,k<<1|1);
    else change(L,mid,num,k<<1),change(mid+1,R,num,k<<1|1);
  pushup(k);
}
int query(int L,int R,int k=1){
  if (L==tr[k].l&&R==tr[k].r) return tr[k].min;
  pushdown(k);
  int mid=tr[k].l+tr[k].r>>1;
  if (R<=mid) return query(L,R,k<<1);
  else if (L>mid) return query(L,R,k<<1|1);
    else return min(query(L,mid,k<<1),query(mid+1,R,k<<1|1));
}
void Change(int u,int v,int num){
  while (nod[u].top^nod[v].top){
    if (nod[nod[u].top].deep<nod[nod[v].top].deep) swap(u,v);
    change(nod[nod[u].top].dfn,nod[u].dfn,num);
    u=nod[nod[u].top].dad;
  }
  if (nod[u].deep>nod[v].deep) swap(u,v);
  change(nod[u].dfn,nod[v].dfn,num);
}
int LCA(int u,int v){
  while (nod[u].top^nod[v].top)
    if (nod[nod[u].top].deep>nod[nod[v].top].deep) u=nod[nod[u].top].dad;
    else v=nod[nod[v].top].dad;
  return nod[u].deep<nod[v].deep?u:v;
}
int Query(int x){
  if (x==now) return tr[1].min;
  if (LCA(now,x)^x) return query(nod[x].dfn,nod[x].low);
  int u=now,mi=INF,depth=nod[u].deep-nod[x].deep-1;
  for (int i=C;i>=0;i--)      //这种地方不要贪图方便把i>=0写成i,不然会死的很惨
    if (depth&1<<i) u=nod[u].grand[i];
  if (nod[u].dfn>1) mi=min(mi,query(1,nod[u].dfn-1));
  if (nod[u].low<n) mi=min(mi,query(nod[u].low+1,n));
  return mi;
}
Abigail into(){
  scanf("%d%d",&n,&m);
  int x,y;
  for (int i=1;i<n;i++){
    scanf("%d%d",&x,&y);
    ins(x,y);ins(y,x);
  }
  for (int i=1;i<=n;i++)
    scanf("%d",&nod[i].v);
  scanf("%d",&now);
}
Abigail work(){
  dfs1(now,0);
  dfs2(now,now);
  build(1,n);
  int opt,x,y,v;
  for (int i=1;i<=m;i++){
    scanf("%d",&opt);
    switch (opt){
      case 1:scanf("%d",&now);
             break;
      case 2:scanf("%d%d%d",&x,&y,&v);
             Change(x,y,v);
             break;
      case 3:scanf("%d",&x);
             printf("%d\n",Query(x));
             break;
    }
  }
}
Abigail outo(){
}
int main(){
  into();
  work();
  outo();
  return 0;
}

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/81037022