【BZOJ】3083: 遥远的国度 -树剖&线段树

传送门:bzoj3083

题解

这篇题解,必须要写(这道简单题真的坑到我了)…好久没打树剖的下场。
除开换根都是裸树剖+线段树。
以1号节点为根建树。换根可以 O(1) O ( 1 ) 换,只需要在查询的时候讨论一下:
若当前查询的节点 id i d 不在1到 root r o o t 的路径上,直接查询子树信息( dfs d f s 序上的 in,out i n , o u t )。
反之,当前以 id i d 为根的子树便是整棵树去掉以 x x root r o o t 方向的第一个子节点 为根的子树,跳重链到这个子节点 x x ′ 即可, id i d 子树 dfs d f s 区间便为 [1,in[x]1][out[x]+1,n] [ 1 , i n [ x ′ ] − 1 ] ∪ [ o u t [ x ′ ] + 1 , n ]
这里需要特殊判断一种情况:当 id==root i d == r o o t 时,子树为 [1,n] [ 1 , n ] (不然会WA得很惨TAT)。
注意几点(主要是必须熟练打板):

  1. 树剖第二遍 dfs d f s 时标记 dfs d f s 序,不是第一遍(会导致重链 dfs d f s 序不连续)
  2. 情况要讨论完整,多想想特殊情况
  3. 0<所有权值<=2^31!刚好爆 int i n t

总之做这道题的体验十分不好TAT,以后要多做DS,锻炼码力QAQ


代码

#include
#define gc getchar()
#define si isdigit(c)
#define lc k<<1
#define rc k<<1|1
#define mid (((l)+(r))>>1)
using namespace std;
typedef long long ll;
const int N=1e5+10;
const ll inf=1LL<<33;
int n,m,in[N],ot[N],d[N],rv[N],dfn;
int head[N],to[N<<1],nxt[N<<1],tot;
int rt,son[N],sz[N],top[N],f[N];
ll a[N],mn[N<<2],st[N<<2],ans;
char c;
template<class T>
inline void rd(T &x)
{
    c=gc;x=0;
    for(;!si;c=gc);
    for(;si;c=gc) x=x*10+(c^48);
}

inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}

inline void dfs(int x)
{
    int i,j;
    sz[x]=1;
    for(i=head[x];i;i=nxt[i]){
        j=to[i];if(j==f[x]) continue;
        f[j]=x;d[j]=d[x]+1;dfs(j);sz[x]+=sz[j];
        if(sz[j]>sz[son[x]]) son[x]=j;
    }
}

inline void dfss(int x,int tp)
{
    top[x]=tp;in[x]=++dfn;rv[dfn]=x;
    if(son[x]) dfss(son[x],tp);
    int i,j;
    for(i=head[x];i;i=nxt[i]){
        j=to[i];if(j==f[x] || j==son[x]) continue;
        dfss(j,j);
    }
    ot[x]=dfn;
}

inline void pushup(int k)
{mn[k]=min(mn[lc],mn[rc]);}

inline void pushdown(int k)
{
    if(!st[k]) return;
    st[lc]=st[rc]=st[k];
    mn[lc]=mn[rc]=st[k];
    st[k]=0;
}

inline void build(int k,int l,int r)
{
    if(l==r){mn[k]=a[rv[l]];return;}
    build(lc,l,mid);build(rc,mid+1,r);
    pushup(k);
}

inline void sett(int k,int l,int r,int L,int R,ll vv)
{
    if(L<=l && r<=R) {st[k]=mn[k]=vv;return;}
    pushdown(k);
    if(L<=mid) sett(lc,l,mid,L,R,vv);
    if(R>mid) sett(rc,mid+1,r,L,R,vv);
    pushup(k);
}

inline ll ask(int k,int l,int r,int L,int R)
{
    if(L>R) return inf;
    if(L<=l && r<=R) return mn[k];
    pushdown(k);ll re=inf;
    if(L<=mid) re=ask(lc,l,mid,L,R);
    if(R>mid) re=min(re,ask(rc,mid+1,r,L,R));
    return re;
}


inline void change(int x,int y,ll z)
{
    for(;top[x]!=top[y];x=f[top[x]]){
        if(d[top[x]]1,1,n,in[top[x]],in[x],z);
    }
    if(d[x]1,1,n,in[y],in[x],z);
}

int main(){
    int op,i,x,y;ll z;
    rd(n);rd(m);
    for(i=1;ifor(i=1;i<=n;++i) rd(a[i]);
    dfs(1);dfss(1,1);
    build(1,1,n);
    rd(rt);
    for(;m;--m){
        rd(op);
        if(op==1) rd(rt);
        else if(op==2){
            rd(x);rd(y);rd(z);
            change(x,y,z);
        }else{
           rd(x);
           if(x==rt) ans=ask(1,1,n,1,n);
           else if((in[x]>=in[rt] && ot[x]<=ot[rt]) || (ot[x]ot[rt])) 
            ans=ask(1,1,n,in[x],ot[x]);
           else{
              y=rt;
              for(;top[y]!=top[x] && f[top[y]]!=x;y=f[top[y]]);
              if(top[x]==top[y]) y=son[x];else y=top[y];
              ans=min(ask(1,1,n,1,in[y]-1),ask(1,1,n,ot[y]+1,n));
            }
           printf("%lld\n",ans);
        }
    }
 }

你可能感兴趣的:(树链剖分)