[HAOI2015]树上操作(树链剖分)

Posted hsez-cyx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[HAOI2015]树上操作(树链剖分)相关的知识,希望对你有一定的参考价值。

[HAOI2015]树上操作(luogu)

Description

 

题目描述

 

有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个操作,分为三种:

  • 操作 1 :把某个节点 x 的点权增加 a 。
  • 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
  • 操作 3 :询问某个节点 x 到根的路径中所有点的点权和。

 

输入格式

 

第一行包含两个整数 N, M 。表示点数和操作数。
接下来一行 N 个整数,表示树中节点的初始权值。
接下来 N-1 行每行两个正整数 from, to , 表示该树中存在一条边 (from, to) 。
再接下来 M 行,每行分别表示一次操作。其中第一个数表示该操作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。

 

输出格式

 

对于每个询问操作,输出该询问的答案。答案之间用换行隔开。

Solution

操作 1:线段树中点dfn[x]加a;

操作 2:由树链剖分遍历的顺序可知任何一棵子树中节点的dfs序<=子树的根的dfs序

且最大的dfs序=子树的根的dfs序+子树的大小-1,于是可以直接在线段树上操作;

操作 3:简化版树剖求和模板

Code

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <vector>
using namespace std;
const int N=1e5+10;
struct node
{
    int l,r,lc,rc;
    long long sum,delay;
}f[N*2];
int n,u,v,m,deep[N],tot,si[N],top[N],d[N],t,
fa[N],dfn[N],cnt,rt,son[N],re[N],opt,x;
long long a;
char s[100];
vector <int> link[N];
void dfs1(int u,int f)
{
    si[u]=1,fa[u]=f,deep[u]=deep[f]+1;
    int size=link[u].size();
    for(int i=0;i<size;i++)
    {
        int v=link[u][i];
        if(v!=f)
        {
            dfs1(v,u);
            si[u]+=si[v];
            if(son[u]==0 || si[son[u]]<si[v]) son[u]=v;
        }
    }
}
void dfs2(int u,int f)
{
    dfn[u]=++tot,re[tot]=u;
    if(son[u]!=0)
    {
        top[son[u]]=top[u],dfs2(son[u],u);
        int size=link[u].size();
        for(int i=0;i<size;i++)
        {
            int v=link[u][i];
            if(v!=f && v!=son[u])
                top[v]=v,dfs2(v,u);
        }
    }    
}
long long gel(int g)
{
    return f[g].r-f[g].l+1;
}
void push_up(int g)
{
    int lc=f[g].lc,rc=f[g].rc;
    if(lc==0) return ;
    f[g].sum=f[lc].sum+f[rc].sum+gel(g)*f[g].delay;
}
void push_down(int g)
{
    int lc=f[g].lc,rc=f[g].rc;
    if(lc==0 || f[g].delay==0) return ;
    f[lc].delay+=f[g].delay,f[rc].delay+=f[g].delay;
    f[lc].sum+=gel(lc)*f[g].delay,f[rc].sum+=gel(rc)*f[g].delay;
    f[g].delay=0;
}
void build(int &g,int l,int r)
{
    g=++cnt;
    f[g].l=l,f[g].r=r;
    if(l==r)
    {
        f[g].sum=d[re[l]];
        return ;
    }
    int mid=(l+r)>>1;
    build(f[g].lc,l,mid);
    build(f[g].rc,mid+1,r);
    push_up(g);
}
void add(int g,int l,int r,int k)
{
    if(f[g].l>=l && f[g].r<=r)
        f[g].sum+=gel(g)*k,f[g].delay+=k;
    else
    {
        int mid=(f[g].l+f[g].r)/2;
        if(r<=mid) add(f[g].lc,l,r,k);
        else if(l>mid) add(f[g].rc,l,r,k);
        else add(f[g].lc,l,mid,k),add(f[g].rc,mid+1,r,k);
        push_up(g);
    }
}
long long get(int g,int l,int r)
{
    if(f[g].l>=l && f[g].r<=r)
        return f[g].sum;
    else
    {
        push_down(g);
        int mid=(f[g].l+f[g].r)/2;
        if(r<=mid) return get(f[g].lc,l,r);
        else if(l>mid) return get(f[g].rc,l,r);
        else return get(f[g].lc,l,mid)+get(f[g].rc,mid+1,r);
    }
}
long long Get(int x)
{
    long long sum=0;
    while(x!=0)
    {
        sum+=get(rt,dfn[top[x]],dfn[x]);
        x=fa[top[x]];
    }
    return sum;
}
//操作 1 :把某个节点 x 的点权增加 a 。
//操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
//操作 3 :询问某个节点 x 到根的路径中所有点的点权和。
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&d[i]);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        link[u].push_back(v);
        link[v].push_back(u);
    }
    dfs1(1,0);
    top[1]=1,dfs2(1,0);
    build(rt,1,tot);
    while(m--)
    {
        scanf("%d%d",&opt,&x);
        if(opt==3) printf("%lld
",Get(x));
        else
        {
            scanf("%lld",&a);
            if(a!=0)
            {
                if(opt==1) add(rt,dfn[x],dfn[x],a);
                else if(opt==2) add(rt,dfn[x],dfn[x]+si[x]-1,a);
            }
        }
    }
    return 0;
}

 

 

 

 

以上是关于[HAOI2015]树上操作(树链剖分)的主要内容,如果未能解决你的问题,请参考以下文章

[HAOI2015]树上操作(树链剖分)

bzoj 4034: [HAOI2015]树上操作 树链剖分+线段树

BZOJ4034[HAOI2015]树上操作 树链剖分+线段树

P3178 [HAOI2015]树上操作 (树链剖分模版题)

4034. [HAOI2015]树上操作树链剖分

bzoj 4034: [HAOI2015]树上操作——树链剖分