[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\n",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;
}

原文地址:https://www.cnblogs.com/hsez-cyx/p/12251267.html

时间: 2024-07-30 22:22:56

[HAOI2015]树上操作(树链剖分)的相关文章

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

4034: [HAOI2015]树上操作 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 4352  Solved: 1387[Submit][Status][Discuss] Description 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有

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

[BZOJ4034][HAOI2015]树上操作 Description 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有点的点权和. Input 第一行包含两个整数 N, M .表示点数和操作数. 接下来一行 N 个整数,表示树中节点的初始权值. 接下来 N-1 行每行三个正整数 fr, to

[HAOI2015]树上操作 -树链剖分

1963. [HAOI2015]树上操作 [题目描述] 有一棵点数为N的树,以点1为根,且树点有权值.然后有M个操作,分为三种: 操作1:把某个节点x的点权增加a. 操作2:把某个节点x为根的子树中所有点的点权都增加a. 操作3:询问某个节点x到根的路径中所有点的点权和. [输入格式] 第一行两个整数N,M,表示点数和操作数. 接下来一行N个整数,表示树中节点的初始权值. 接下来N-1行每行两个正整数fr,to,表示该树中存在一条边(fr,to). 再接下来M行,每行分别表示一次操作.其中第一个

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

Description 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个操作,分为三种:操作 1 :把某个节点 x 的点权增加 a .操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a .操作 3 :询问某个节点 x 到根的路径中所有点的点权和.Input 第一行包含两个整数 N, M .表示点数和操作数.接下来一行 N 个整数,表示树中节点的初始权值.接下来 N-1 行每行三个正整数 fr, to , 表示该树中存在一条边 (fr, to) .再接下来 M 行,

bzoj4034 树上操作 树链剖分+线段树

题目传送门 题目大意: 有一棵点数为 N 的树,以点 1 为根,且树点有权.然后有 M 个操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有点的点权和. 思路: 由于是在刷dfs序专题的时候碰到这题,所以思路被限制了,没想树链剖分的东西,没能做出来,后来发现了一个 大佬的博客,发现也是可以做的,但是这个做法看不懂...留坑 现在用树链剖分的方法,每个点的权值就是点本身

cogs 1963. [HAOI 2015] 树上操作 树链剖分+线段树

1963. [HAOI 2015] 树上操作 ★★★☆   输入文件:haoi2015_t2.in   输出文件:haoi2015_t2.out   简单对比时间限制:1 s   内存限制:256 MB [题目描述] 有一棵点数为N的树,以点1为根,且树点有权值.然后有M个操作,分为三种: 操作1:把某个节点x的点权增加a. 操作2:把某个节点x为根的子树中所有点的点权都增加a. 操作3:询问某个节点x到根的路径中所有点的点权和. [输入格式] 第一行两个整数N,M,表示点数和操作数. 接下来一

bzoj4034: [HAOI2015]树上操作(树剖)

4034: [HAOI2015]树上操作 题目:传送门 题解: 树剖裸题: 麻烦一点的就只有子树修改(其实一点也不),因为子树编号连续啊,直接改段(记录编号最小和最大) 开个long long 水模版 代码: 1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std

树链剖分详解

前言 树链剖分是什么? 树链剖分,说白了就是一种让你代码不得不强行增加1k的数据结构-dms 个人理解:+1:joy: 有什么用? 证明出题人非常毒瘤 可以非常友(bao)好(li)的解决一些树上问题:grimacing: (友情提示:学树链剖分之前请先掌握线段树) 核心思想 树链剖分的思想比较神奇 它的思想是:把一棵树拆成若干个不相交的链,然后用一些数据结构去维护这些链 那么问题来了 如何把树拆成链? 首先明确一些定义 重儿子:该节点的子树中,节点个数最多的子树的根节点(也就是和该节点相连的点

hdu 4897 树链剖分(重轻链)

Little Devil I Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 894    Accepted Submission(s): 296 Problem Description There is an old country and the king fell in love with a devil. The devil

【模板】重链剖分(树链剖分)

我们知道对一列数进行区间或单点加减,乘除和区间求值等操作可以用线段树或树状数组 那么,如何对带权树上一条路径中的数进行这样的操作呢? 此时就用到了线段树的树上版——树链剖分 原文地址:https://www.cnblogs.com/5454tfggg/p/12393241.html