树链剖分,理解了也就这么点事 也就写了百来行嘛
两次DFS,第一次处理出树的父子关系以及求出每个节点的子节点数,第二次处理重链与轻链。
把树分为重链和轻链,选择子节点最多的儿子继承重链,其余另开一条链。
一条链上的点在线段树中分配连续的位置,方便求和等操作。
参考了下黄学长的代码。。
查了几个小时结果是因为一个类型打错了差点弄出神经病。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#define maxn 100010
#define maxe 200100
#define ll long long
using namespace std;
struct data{
int v,next;
}e[maxe];
int n,m,ed,sz;
int belong[maxn],far[maxn],head[maxn],v[maxn],size[maxn],fa[maxn];
int pos[maxn];
ll tree[maxe*2],sum[maxe*2];
int read()
{
char ch=getchar();
int x=0,f=1;
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();}
return x*f;
}
void insert(int x,int y){
e[++ed].v=y;e[ed].next=head[x];head[x]=ed;
e[++ed].v=x;e[ed].next=head[y];head[y]=ed;
}
void init()
{
n=read();m=read();
for(int i=1;i<=n;++i)v[i]=read();
for(int i=1;i<n;++i){
int x=read();int y=read();
insert(x,y);
}
}
void dfs1(int x)
{
size[x]=1; //求每一个节点的子节点数,即每一个节点的"重量"
for(int i=head[x];i;i=e[i].next)
if(e[i].v!=fa[x])
{
fa[e[i].v]=x;
dfs1(e[i].v);
size[x]+=size[e[i].v];
far[x]=max(far[x],far[e[i].v]);
}
}
void dfs2(int x,int lian)
{
belong[x]=lian;
pos[x]=far[x]=++sz;
int k=0;
for(int i=head[x];i;i=e[i].next)
{
if(e[i].v!=fa[x]&&size[k]<size[e[i].v])k=e[i].v; //选择最"重"的子结点
}
if(k)
{
dfs2(k,lian);far[x]=max(far[x],far[k]); /**继承重链**/
} //far[x]意为以x为父节点的最远的节点编号,在线段树中方便2号修改
for(int i=head[x];i;i=e[i].next)
{
if(e[i].v!=fa[x]&&k!=e[i].v)
{
dfs2(e[i].v,e[i].v); //另开新链
far[x]=max(far[x],far[e[i].v]);
}
}
}
void push(int t,int l,int r)
{
if(l==r)return;
ll x=tree[t];
tree[t]=0;
int mid=(l+r)>>1;
tree[t<<1]+=x;tree[t<<1|1]+=x;
sum[t<<1]+=x*(mid-l+1);
sum[t<<1|1]+=x*(r-mid);
}
void add(int t,int l,int r,int x,int y,ll num)
{
if(tree[t])push(t,l,r);
if(l==x&&r==y)
{
tree[t]+=num;sum[t]+=num*(r-l+1);return;
}
int mid=(l+r)>>1;
if(x<=mid)add(t<<1,l,mid,x,min(mid,y),num);
if(y>mid)add(t<<1|1,mid+1,r,max(mid+1,x),y,num);
sum[t]=sum[t<<1]+sum[t<<1|1];
}
ll q(int t,int l,int r,int x,int y)
{
if(tree[t])push(t,l,r);
if(l==x&&r==y)return sum[t];
int mid=(l+r)>>1;
ll ans=0;
if(x<=mid)ans+=q(t<<1,l,mid,x,min(mid,y));
if(y>mid)ans+=q(t<<1|1,mid+1,r,max(x,mid+1),y);
return ans;
}
ll q(int x)
{
ll ans=0;
while(belong[x]!=1) //如果没有到根节点所在的重链
{
ans+=q(1,1,n,pos[belong[x]],pos[x]); //求这条链上的和
x=fa[belong[x]]; //x跳转到该链的父节点继续操作
}
ans+=q(1,1,n,1,pos[x]);
return ans;
}
void solve()
{
for(int i=1;i<=m;++i)
{
int pr=read(),x=read();
if(pr==1){int a=read();add(1,1,n,pos[x],pos[x],a);}
else
if(pr==2){int a=read();add(1,1,n,pos[x],far[x],a);}
else
{printf("%lld\n",q(x));}
}
}
int main()
{
init();
dfs1(1);
dfs2(1,1);
for(int i=1;i<=n;++i)add(1,1,n,pos[i],pos[i],v[i]);
solve();
return 0;
}