原题链接: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");
}

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

被折叠的 条评论
为什么被折叠?



