Jamie and Tree[CF916E]

题意:

有一棵n个点的树,每个节点上有一个权值wi,最开始根为1号点.现在有3种
类型的操作:
• 1 root, 表示将根设为root.
• 2 u v x, 设u, v的最近公共祖先为p, 将p的子树中的所有点的权值加上x.
• 3 u, 查询u的子树中的所有点的权值和.
对于每个3操作,输出答案.

题解:

须知知识:线段树,树链剖分

  1. 如果真的换根太麻烦了,所以我们每次只记录新根,并看看新根在后续操作中会有什么影响

  2. 如果在没有换根的情况下,操作2很容易实现,但是因为现在root已经发生改变所以我们需要特判。
    lca1=lca(u,v)
    lca2=lca(root,v)
    lca3=lca(root,u)
    lca=max(dep[lca1],dep[lca2],dep[lca3])
    我们要先记录lca1,lca2,lca3中dep最深的
    如果root不在lca的子树里,那直接在区间[l[lca],r[lca]]内更新x即可
    如果root等于lca,那么就是全树更新
    如果root在lca的子树上,先把整个树更新,然后找到root到lca的路径与lca的儿子节点,更新子树-x
    相当于先全部+x,然后将不需要修改的部分再-x

    if(root==lca1)tree.update(1,1,n,1,n,x);
             else if(dfn[rt]<dfn[lca1]||dfn[rt]>dfn[lca1]+size[lca1]-1)
                 tree.update(1,1,n,dfn[lca1],dfn[lca1]+size[lca1]-1,x);
             else 
             {
                 lca1=change(lca1);
                 tree.update(1,1,n,1,n,x);
                 tree.update(1,1,x,dfn[lca1],dfn[lca1]+size[lca1]-1,-x);
             }
  3. 对于操作三,我们要判断root与v的关系,如果root不在v的子树上,那就正常操作sum(l[v],r[v])
    如果root就是v,更新整个子树
    但如果root在v的子树上,那么其实和操作2的第三个情况类似,先求所以树的权值,然后减去不需要算的部分。这里不需要算的部分是root到lca的路径与lca的儿子节点X
    (为什么是这个点?看着图仔细想想)
    在这里插入图片描述

if(u==rt)sum=tree.query(1,1,n,1,n);
else if(dfn[rt]<dfn[u]||dfn[rt]>dfn[u]+size[u]-1)
    sum=tree.query(1,1,n,dfn[u],dfn[u]+size[u]-1);
else 
    {
        u=change(u);
        sum+=tree.query(1,1,n,1,n);
        sum-=tree.query(1,1,n,dfn[u],dfn[u]+size[u]-1);
    }

代码:

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;
using namespace std;

template<typename T>void read(T &x){
    T _=0,mul=1;
    char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')mul=-1;ch=getchar();}
    while(isdigit(ch))_=(_<<1)+(_<<3)+(ch^'0'),ch=getchar();
    x=_*mul;
}


const int maxn=3e5+10;

struct Segment_Tree{
#define mid ((l+r)>>1)
#define lc (rt<<1)
#define rc (rt<<1|1)
#define lson lc,l,mid
#define rson rc,mid+1,r
    ll sum[maxn<<2],tag[maxn<<2];
    void pushdown(int rt,int l,int r){
        sum[lc]+=tag[rt]*(mid-l+1);tag[lc]+=tag[rt];
        sum[rc]+=tag[rt]*(r-mid);  tag[rc]+=tag[rt];
        tag[rt]=0;
    }
    void update(int rt,int l,int r,int L,int R,ll x){
        if(L<=l && r<=R){
            sum[rt]+=(r-l+1)*x;
            tag[rt]+=x;
            return;
        }
        if(tag[rt])pushdown(rt,l,r);
        if(L<=mid)update(lson,L,R,x);
        if(R>=mid+1)update(rson,L,R,x);
        sum[rt]=sum[lc]+sum[rc];
    }
    ll query(int rt,int l,int r,int L,int R){
        if(L<=l && r<=R)return sum[rt];
        ll ret=0ll;
        if(tag[rt])pushdown(rt,l,r);
        if(L<=mid)ret+=query(lson,L,R);
        if(R>=mid+1)ret+=query(rson,L,R);
        return ret;
    }
}T;

int n,q,root,w[maxn],cnt;
int to[maxn<<1],last[maxn<<1],beg[maxn],cnte;
int fa[maxn],son[maxn],top[maxn],siz[maxn],dep[maxn],cnt_dfn,dfn[maxn];

void add(int u,int v){
    last[++cnte]=beg[u];
    beg[u]=cnte;
    to[cnte]=v;
}

void dfs1(int u,int f){
    fa[u]=f;
    siz[u]=1;
    dep[u]=dep[f]+1;
    for(int i=beg[u];i;i=last[i]){
        if(to[i]==f)continue;
        dfs1(to[i],u);
        siz[u]+=siz[to[i]];
        if(siz[to[i]]>siz[son[u]])
            son[u]=to[i];
    }
}

void dfs2(int u,int t){
    dfn[u]=++cnt_dfn;
    top[u]=t;
    if(son[u])dfs2(son[u],t);
    for(int i=beg[u];i;i=last[i]){
        if(to[i]==fa[u] || to[i]==son[u])continue;
        dfs2(to[i],to[i]);
    }
}

int find(int u,int v){
    while(top[u]!=top[v]){
        ++cnt;
        if(dep[top[u]]<dep[top[v]])swap(u,v);
        u=fa[top[u]];
    }
    return dep[u]<dep[v] ? u : v;
}

int change(int u){
    int v=root;
    while(top[v]!=top[u]){
        if(fa[top[v]]==u)return top[v];
        v=fa[top[v]];
    }
    return son[u];
}

void init(){
    read(n); read(q);
    root=1;
    REP(i,1,n)read(w[i]);
    REP(i,1,n-1){
        int u,v;
        read(u); read(v);
        add(u,v);
        add(v,u);
    }
    dfs1(1,0);
    dfs2(1,1);
    REP(i,1,n)T.update(1,1,n,dfn[i],dfn[i],w[i]);
}

int main(){

    init();
    REP(i,1,q){
        int ty,u,v;
        ll x;
        read(ty);
        if(ty==1){
            read(u);
            root=u;
        }
        else if(ty==2){
            read(u); read(v); read(x);
            int lca=find(u,v),tmp;
            if(dep[tmp=find(u,root)]>dep[lca])lca=tmp;
            if(dep[tmp=find(v,root)]>dep[lca])lca=tmp;
            if(lca==root)T.update(1,1,n,1,n,x);
            else if(dfn[root]<dfn[lca] || dfn[root]>dfn[lca]+siz[lca]-1)
                T.update(1,1,n,dfn[lca],dfn[lca]+siz[lca]-1,x);
            else{
                lca=change(lca);
                T.update(1,1,n,1,n,x);
                T.update(1,1,n,dfn[lca],dfn[lca]+siz[lca]-1,-x);
            }
        }
        else{
            ll sum=0ll;
            read(u);
            if(u==root)sum=T.query(1,1,n,1,n);
            else if(dfn[root]<dfn[u] || dfn[root]>dfn[u]+siz[u]-1)
                sum=T.query(1,1,n,dfn[u],dfn[u]+siz[u]-1);
            else{
                u=change(u);
                sum+=T.query(1,1,n,1,n);
                sum-=T.query(1,1,n,dfn[u],dfn[u]+siz[u]-1);
            }
            cout<<sum<<endl;
        }
    }
    return 0;
}