LOJ 139 树剖

题目描述

这是一道模板题。

给定一棵 n 个节点的树,初始时该树的根为 1 号节点,每个节点有一个给定的权值。下面依次进行 m 个操作,操作分为如下五种类型:

换根:将一个指定的节点设置为树的新根。

修改路径权值:给定两个节点,将这两个节点间路径上的所有节点权值(含这两个节点)增加一个给定的值。

修改子树权值:给定一个节点,将以该节点为根的子树内的所有节点权值增加一个给定的值。

询问路径:询问某条路径上节点的权值和。

询问子树:询问某个子树内节点的权值和。

输入格式

第一行为一个整数 nnn,表示节点的个数。

第二行 nnn 个整数表示第 iii 个节点的初始权值 ai​​ 。

第三行 n−1 个整数,表示 i+1号节点的父节点编号 fi+1 (1⩽fi+1⩽n)。

第四行一个整数 m,表示操作个数。

接下来 m 行,每行第一个整数表示操作类型编号:(1⩽u,v⩽n)

若类型为 1,则接下来一个整数 u,表示新根的编号。

若类型为 2,则接下来三个整数 u,v,k,分别表示路径两端的节点编号以及增加的权值。

若类型为 3,则接下来两个整数 u,k,分别表示子树根节点编号以及增加的权值。

若类型为 4,则接下来两个整数 u,v,表示路径两端的节点编号。

若类型为 5,则接下来一个整数 u,表示子树根节点编号。

输出格式

对于每一个类型为 4 或 5 的操作,输出一行一个整数表示答案。

思路

板子题也没什么难度,就是新奇的换根,一下子没想到。但也不难,不需要真正地换,记录一下root,只要root不在当前点的子树中就不会有影响。root在子树中时整棵树拿来加,再把当前点的包含root的儿子节点的子树减掉就好了。

代码

#include<cstdio>
#include<string>
#define ls (o<<1)
#define rs (o<<1|1)
#define mid (L+R>>1)
using namespace std;
typedef long long LL;
const int maxn=2e5+5;
int N,M,tot,cnt,root,a[maxn];
int son[maxn],fa[maxn],nxt[maxn],lnk[maxn];
int dep[maxn],top[maxn],id[maxn],s[maxn],sz[maxn];
struct Seg {
    LL tag[maxn<<2],sum[maxn<<2],a[maxn];
    inline void pushup(int o) {sum[o]=sum[ls]+sum[rs];}
    inline void pushdown(int o,int L,int R) {
        if (tag[o])
            tag[ls]+=tag[o],sum[ls]+=tag[o]*(mid-L+1),
            tag[rs]+=tag[o],sum[rs]+=tag[o]*(R-mid);
        tag[o]=0;
    }
    inline void Build(int o,int L,int R) {
        if (L==R) {sum[o]=a[L];return;}
        Build(ls,L,mid),Build(rs,mid+1,R);
        pushup(o);
    }
    void Updata(int o,int L,int R,int ql,int qr,LL dat) {
        if (ql<=L&&R<=qr) {tag[o]+=dat,sum[o]+=dat*(R-L+1);return;}
        if (ql>R||L>qr) return;
        pushdown(o,L,R);
        Updata(ls,L,mid,ql,qr,dat),Updata(rs,mid+1,R,ql,qr,dat);
        pushup(o);
    }
    LL Query(int o,int L,int R,int ql,int qr) {
        if (ql<=L&&R<=qr) return sum[o];
        if (ql>R||L>qr) return 0;
        pushdown(o,L,R);
        return Query(ls,L,mid,ql,qr)+Query(rs,mid+1,R,ql,qr);
    }
} T;
inline int read() {
    int ret=0,f=1;char ch=getchar();
    for (; !isdigit(ch); ch=getchar()) if (ch=='-') f=-f;
    for (; isdigit(ch); ch=getchar()) ret=ret*10+ch-48;
    return ret*f;
}
inline void add_edge(int x,int y) {son[++tot]=y,nxt[tot]=lnk[x],lnk[x]=tot;}
void dfs1(int x,int pre) {
    fa[x]=pre,dep[x]=dep[pre]+1,sz[x]=1;
    for (int k=lnk[x]; k; k=nxt[k]) if (son[k]^pre) {
        dfs1(son[k],x);
        sz[x]+=sz[son[k]];
        if (sz[son[k]]>sz[s[x]]) s[x]=son[k];
    }
}
void dfs2(int x,int tp) {
    top[x]=tp,id[x]=++cnt,T.a[cnt]=a[x];
    if (s[x]) dfs2(s[x],tp);
    for (int k=lnk[x]; k; k=nxt[k]) if (son[k]^fa[x]&&son[k]^s[x])
        dfs2(son[k],son[k]);
}
inline bool check(int x,int y) {
    return (sz[x]+id[x]-1>=id[y]&&id[x]<=id[y])?1:0;
}
inline void Modify_Path(LL k,int x,int y) {
    while (top[x]^top[y]) {
        if (dep[top[x]]<dep[top[y]]) swap(x,y);
        T.Updata(1,1,N,id[top[x]],id[x],k);
        x=fa[top[x]];
    }
    if (dep[x]<dep[y]) swap(x,y);
    T.Updata(1,1,N,id[y],id[x],k);
}
inline void Modify_Tree(LL K,int x) {
    T.Updata(1,1,N,1,N,K);
    if (x==root) return;
    for (int k=lnk[x]; k; k=nxt[k]) if (check(son[k],root))
        T.Updata(1,1,N,id[son[k]],id[son[k]]+sz[son[k]]-1,-K);
}
inline void Query_Path(int x,int y) {
    LL ret=0;
    while (top[x]^top[y]) {
        if (dep[top[x]]<dep[top[y]]) swap(x,y);
        ret+=T.Query(1,1,N,id[top[x]],id[x]);
        x=fa[top[x]];
    }
    if (dep[x]<dep[y]) swap(x,y);
    printf("%lld\n",ret+T.Query(1,1,N,id[y],id[x]));
}
inline void Query_Tree(int x) {
    LL ret=T.sum[1];
    if (x^root) for (int k=lnk[x]; k; k=nxt[k]) if (check(son[k],root))
        ret-=T.Query(1,1,N,id[son[k]],id[son[k]]+sz[son[k]]-1);
    printf("%lld\n",ret);
}
int main() {
    N=read(),root=1;
    for (int i=1; i<=N; i++) a[i]=read();
    for (int i=2; i<=N; i++) add_edge(read(),i);
    dfs1(1,0),dfs2(1,1),T.Build(1,1,N);
    M=read();
    while (M--)
        switch (read()) {
            case 1: root=read();break;
            case 2: Modify_Path(read(),read(),read());break;
            case 3: Modify_Tree(read(),read());break;
            case 4: Query_Path(read(),read());break;
            case 5: Query_Tree(read());
        }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zj_js_zxb/article/details/80670112
今日推荐