Luogu3178 [HAOI2015]树上操作

这篇博客详细介绍了如何解决一道关于树上操作的竞赛编程题目。题目涉及树形结构、区间修改和查询操作,博主通过建立树状数组并采用线段树的数据结构来高效地处理点权增加和子树点权增加的操作,并实现询问路径点权和的功能。博主强调了在编程过程中将`int`类型改为`long long`以避免溢出的重要性,并提供了完整的AC代码示例。

原题链接:https://www.luogu.com.cn/problem/P3178

树上操作

题目描述

有一棵点数为 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 ) 。

输出格式

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

输入输出样例

输入 #1
5 5
1 2 3 4 5
1 2
1 4
2 3
2 5
3 3
1 2 1
3 5
2 1 2
3 3
输出 #1
6
9
13

说明/提示

对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不会超过 10^6 。

题解

之所以这种板子级别的题都要写篇博客,除了水篇博客以外, 就是想说明int改成long long一定要改干净,否则就是贴模板还 W A 5 \mathcal{WA}5 WA5次惨案重现😅。

#include<bits/stdc++.h>
#define ls v<<1
#define rs v<<1|1
using namespace std;
const int M=1e5+5;
struct node{int le,ri;long long sum,delta;}tree[M<<2];
int n,m,tot,val[M],val2[M],dad[M],eld[M],dep[M],siz[M],top[M],id[M];
vector<int>edge[M];
void up(int v){tree[v].sum=tree[ls].sum+tree[rs].sum;}
void push(int v,long long delta){tree[v].sum+=1ll*(tree[v].ri-tree[v].le+1)*delta,tree[v].delta+=delta;}
void down(int v){if(tree[v].delta)push(ls,tree[v].delta),push(rs,tree[v].delta);tree[v].delta=0;}
void build(int v,int le,int ri)
{
    tree[v].le=le,tree[v].ri=ri;
    if(le==ri){tree[v].sum=val2[le];return;}
    int mid=le+ri>>1;
    build(ls,le,mid),build(rs,mid+1,ri);
    up(v);
}
void add(int v,int le,int ri,int delta)
{
    if(le<=tree[v].le&&tree[v].ri<=ri){push(v,delta);return;}
    down(v);
    if(le<=tree[ls].ri)add(ls,le,ri,delta);
    if(tree[rs].le<=ri)add(rs,le,ri,delta);
    up(v);
}
long long ask(int v,int le,int ri)
{
    if(le<=tree[v].le&&tree[v].ri<=ri)return tree[v].sum;
    long long r=0;
    down(v);
    if(le<=tree[ls].ri)r=ask(ls,le,ri);
    if(tree[rs].le<=ri)r+=ask(rs,le,ri);
    return r;
}
long long askchain(int x,int y)
{
    long long r=0;
    for(;top[x]!=top[y];x=dad[top[x]])
    {
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        r+=ask(1,id[top[x]],id[x]);
    }
    return r+ask(1,min(id[x],id[y]),max(id[x],id[y]));
}
int dfs1(int v,int f,int d)
{
    dad[v]=f,dep[v]=d,siz[v]=1;
    for(int i=edge[v].size()-1;i>=0;--i)
    {
        if(edge[v][i]==f)continue;
        siz[v]+=dfs1(edge[v][i],v,d+1);
        if(siz[edge[v][i]]>siz[eld[v]])eld[v]=edge[v][i];
    }
    return siz[v];
}
void dfs2(int v,int topf)
{
    id[v]=++tot,val2[id[v]]=val[v],top[v]=topf;
    if(!eld[v])return;
    dfs2(eld[v],topf);
    for(int i=edge[v].size()-1;i>=0;--i)
    if(edge[v][i]!=dad[v]&&edge[v][i]!=eld[v])dfs2(edge[v][i],edge[v][i]);
}
void in()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)scanf("%d",&val[i]);
    for(int i=1,a,b;i<n;++i)scanf("%d%d",&a,&b),edge[a].push_back(b),edge[b].push_back(a);
}
void ac()
{
    dfs1(1,0,1);
    dfs2(1,1);
    build(1,1,n);
    for(int i=1,a,b,c;i<=m;++i)
    {
        scanf("%d%d",&a,&b);
        if(a==1)scanf("%d",&c),add(1,id[b],id[b],c);
        else if(a==2)scanf("%d",&c),add(1,id[b],id[b]+siz[b]-1,c);
        else printf("%lld\n",askchain(1,b));
    }
}
int main()
{
    in(),ac();
    system("pause");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值