[SHOI2012]魔法树——[树链剖分]

【题目描述】

Harry Potter 新学了一种魔法:可以让改变树上的果子个数。满心欢喜的他找到了一个巨大的果树,来试验他的新法术。

这棵果树共有N个节点,其中节点0是根节点,每个节点u的父亲记为fa[u],保证有fa[u] < u。初始时,这棵果树上的果子都被 Dumbledore 用魔法清除掉了,所以这个果树的每个节点上都没有果子(即0个果子)。

不幸的是,Harry 的法术学得不到位,只能对树上一段路径的节点上的果子个数统一增加一定的数量。也就是说,Harry 的魔法可以这样描述:

Add u v d

表示将点u和v之间的路径上的所有节点的果子个数都加上d。

接下来,为了方便检验 Harry 的魔法是否成功,你需要告诉他在释放魔法的过程中的一些有关果树的信息:

Query u

表示当前果树中,以点u为根的子树中,总共有多少个果子?

【输入格式】
第一行一个正整数N (1 ≤ N ≤ 100000),表示果树的节点总数,节点以0,1,…,N − 1标号,0一定代表根节点。

接下来N − 1行,每行两个整数a,b (0 ≤ a < b < N),表示a是b的父亲。

接下来是一个正整数Q(1 ≤ ? ≤ 100000),表示共有Q次操作。

后面跟着Q行,表示各个询问。

【输出格式】
对于所有的Query操作,依次输出询问的答案,每行一个。答案可能会超过2^32 ,但不会超过10^15 。

【样例输入】
4
0 1
1 2
2 3
4
A 1 3 1
Q 0
Q 1
Q 2

【样例输出】
3
3
2

【题意分析】
树链剖分模板题,题目前面一堆废话,但是注意节点从0开始。

Code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAX 200000
using namespace std;
typedef long long ll;

struct Front_Link_Star{
    ll next,to;
}edge[MAX];

ll tree[MAX << 2],lazy[MAX << 2],res;
ll top[MAX],head[MAX],son[MAX],depth[MAX],father[MAX];
ll size[MAX],id[MAX],n,m,cnt,dfn;

inline void Add_Edge(ll u,ll v){
    edge[++cnt].to=v;
    edge[cnt].next=head[u];
    head[u]=cnt;
}

inline void push_down(ll now,ll tl,ll tr){
    ll mid=(tl+tr) >> 1;
    tree[now << 1]+=lazy[now]*(mid-tl+1);
    tree[now << 1|1]+=lazy[now]*(tr-mid);
    lazy[now << 1]+=lazy[now];
    lazy[now << 1|1]+=lazy[now];
    lazy[now]=0;
}

inline void update(ll now,ll tl,ll tr,ll left,ll right,ll change){
    if (right<tl||tr<left)return;
    if (left<=tl&&tr<=right){
        tree[now]+=change*(tr-tl+1);
        lazy[now]+=change;
        return;
    }
    if (lazy[now])push_down(now,tl,tr);
    ll mid=(tl+tr) >> 1;
    update(now << 1,tl,mid,left,right,change);
    update(now << 1|1,mid+1,tr,left,right,change);
    tree[now]=tree[now << 1]+tree[now << 1|1];
}

inline void query(ll now,ll tl,ll tr,ll left,ll right){
    if (right<tl||tr<left)return;
    if (left<=tl&&tr<=right){
        res+=tree[now];
        return;
    }
    if (lazy[now])push_down(now,tl,tr);
    ll mid=(tl+tr) >> 1;
    query(now << 1,tl,mid,left,right);
    query(now << 1|1,mid+1,tr,left,right);
}

inline void Modify_Range(ll x,ll y,ll z){
    while (top[x]!=top[y]){
        if (depth[top[x]]<depth[top[y]])swap(x,y);
        update(1,1,n,id[top[x]],id[x],z);
        x=father[top[x]];
    }
    if (depth[x]>depth[y])swap(x,y);
    update(1,1,n,id[x],id[y],z);
}

inline ll Query_Tree(ll x){
    res=0;
    query(1,1,n,id[x],id[x]+size[x]-1);
    return res;
}

inline void DFS1(ll now,ll fa,ll d){
    father[now]=fa;
    depth[now]=d;
    size[now]=1;
    ll maxson=-1;
    for (register int i=head[now];i;i=edge[i].next){
        ll v=edge[i].to;
        if (v==fa)continue;
        DFS1(v,now,d+1);
        size[now]+=size[v];
        if (maxson<size[v]){
            maxson=size[v];
            son[now]=v;
        }
    }
}

inline void DFS2(ll now,ll top_heavy){
    top[now]=top_heavy;
    id[now]=++dfn;
    if (!son[now])return;
    DFS2(son[now],top_heavy);
    for (register int i=head[now];i;i=edge[i].next){
        ll v=edge[i].to;
        if (v!=father[now]&&v!=son[now])DFS2(v,v);
    }
}

int main(){
    scanf("%lld",&n);
    for (register int i=1;i<n;i++){
        ll x,y;
        scanf("%lld%lld",&x,&y);
        x++;y++;
        Add_Edge(x,y);
        Add_Edge(y,x);
    }
    DFS1(1,0,1);
    DFS2(1,1);
    scanf("%lld",&m);
    while (m--){
        char ch[4];
        ll x,y,z;
        scanf("%s",&ch);
        if (ch[0]=='A'){
            scanf("%lld%lld%lld",&x,&y,&z);
            Modify_Range(x+1,y+1,z);
        }
        if (ch[0]=='Q'){
            scanf("%lld",&x);
            printf("%lld\n",Query_Tree(x+1));
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Mega_Aspirin/article/details/81506008
今日推荐