BZOJ3083 遥远的国度题解(树链剖分+分类讨论)

题目:BZOJ3083.
题目大意:维护一个数据结构,在一棵 n n n个点的树上支持:
1.格式 1 x 1 x 1x,表示将根换成 x x x.
2.格式 2 x y z 2 x y z 2xyz,表示将 x x x y y y的路径上的节点的权值都修改为z.
3.格式 3 x 3 x 3x,表示询问以 x x x为根的子树的点权最小值.
设操作次数为 m m m,则 1 ≤ n , m ≤ 1 0 5 1\leq n,m\leq 10^5 1n,m105.

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

但是有换根操作怎么办?

我们可以很容易的得出一个结论,根是哪一个跟链的关系并不大,树的结构也不会变,但是节点的父子关系会变,也就是说子树查询受到了一定的影响.

我们可以画一张图看看(节点上标的数字不是编号,是权值):
在这里插入图片描述
图画的丑不要介意.

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

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

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

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

我们可以确定这条链,那按照这条链不断往上跳,直到跳到的点的父亲是当前这个点.但是这样时间复杂度没有保证怎么办?

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

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

我们可以肯定的一点是,若 x x x和当前根的LCA是 x x x的话,那么 x 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值