算是树剖模板题,两边dfs加线段树可以解决。
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
#define xx 100005
int size[xx], wson[xx], dep[xx], fa[xx], top[xx];
//x节点的节点数,重儿子,深度,父亲,x所在重链的链头。
int tops[xx], pre[xx], cnt, ta;//dfs序,dfs序的反映射,cnt,ta计数用。
int w[4*xx], n, q; //节点权值,节点数,操作数。
string ad;//操作指令。
struct re
{
int next,head,t;
}e[xx<<2];//存图 。
struct ak
{
int sum,maxv,r,l;
}tree[xx<<2];//存线段树。
void add(int x,int y)
{
e[++ta].t=y;e[ta].next=e[x].head;e[x].head=ta;
e[++ta].t=x;e[ta].next=e[y].head;e[y].head=ta;//建图。
}
void dfs1(int now,int f)
{
size[now]=1;//dfs每个点的子节点数,深度,重链所在节点的重儿子,
for(int i=e[now].head;i;i=e[i].next)
{
int go=e[i].t;
if(go!=f)//因为是双向图,所以要防止无限递归爆栈。
{
dep[go]=dep[now]+1;fa[go]=now;
dfs1(go,now);
size[now]+=size[go];
if(size[go]>size[wson[now]])
wson[now]=go;
//如果go的子节点数比父亲当前的重儿子子节点数多,则go替换成为重儿子。
}
}
}
void dfs2(int now,int tp)
{
tops[now]=++cnt; pre[cnt]=now; top[now]=tp;//记录所有节点的dfs序及其反映射。
if(wson[now]) dfs2( wson[now],tp );//如果有重儿子则继续编号。
for(int i=e[now].head;i;i=e[i].next)
{
int go=e[i].t;
if(go!=fa[now]&&go!=wson[now])
{
dfs2(go,go);//再去拉伸除重儿子外的节点的子节点下的重链。
}
}
}
void build(int d,int l,int r)
{
int mid=(l+r)/2;//线段树建树(dfs序的线段树)。
tree[d].l=l;tree[d].r=r;
if(l==r)
{
tree[d].sum=tree[d].maxv=w[pre[l]];//不同点在是w[pre[l]]不是w[l]。
return;
}
build(d*2,l,mid);build(d*2+1,mid+1,r);
tree[d].sum=tree[d*2].sum+tree[d*2+1].sum;
tree[d].maxv=max(tree[d*2].maxv,tree[d*2+1].maxv);
}
void update(int d,int x,int z)
{
//线段树单点修改操作。
int mid=(tree[d].l +tree[d].r )/2;
if(tree[d].l==tree[d].r)
{
tree[d].sum=tree[d].maxv=z;
return;
}
if(x>mid)
{
update(d*2+1,x,z);
}
else
{
update(d*2,x,z);
}
tree[d].sum=tree[d*2].sum+tree[d*2+1].sum;
tree[d].maxv=max(tree[d*2].maxv,tree[d*2+1].maxv);
//修改受影响区间的sum和maxv。
}
int treesum(int d,int l,int r,int ql,int qr)
{
int mid=(l+r)/2,ans=0;
if (ql<=l&&r<=qr)return tree[d].sum;
if (ql<=mid)ans+=treesum(d*2,l,mid,ql,qr);
if (qr>mid)ans+=treesum(d*2+1,mid+1,r,ql,qr);
return ans;
}
int treemaxv(int d,int ql,int qr)
{
int mid=(tree[d].l+tree[d].r)/2,ans=-1000000;
if (ql<=tree[d].l&&tree[d].r<=qr)return tree[d].maxv;
if (ql<=mid)ans=max(ans,treemaxv(d*2,ql,qr));
if (qr>mid)ans=max(ans,treemaxv(d*2+1,ql,qr));
return ans;
}
int psum(int x,int y)
{
int ans=0;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);//使x存深度较大的节点的序号(是原序号,不是dfs序)。
ans+=treesum(1,1,n,tops[top[x]],tops[x]);
//寻找x顶节点dfs序到x节点dfs序的区间和。
x=fa[top[x]];//x赋值为根节点的父亲节点。
}
if(dep[x]<dep[y]) swap(x,y);
ans+=treesum(1,1,n,tops[y],tops[x]);//如果根节点相同,直接计算。
return ans;
}
int pmax(int x,int y )
{
int ans=-1000000000;//和求和代码很相似,不解释了。
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
ans=max(ans,treemaxv(1,tops[top[x]],tops[x]));
x=fa[top[x]];
}
if(dep[x]<dep[y]) swap(x,y);
ans=max(ans,treemaxv(1,tops[y],tops[x]));
return ans;
}
int main()
{
cin>>n;
for(int i=1;i<=n-1;++i)
{
int u,v;
cin>>u>>v;
add(u,v);//函数直接写的存双向图。
}
for(int i=1;i<=n;++i)
{
cin>>w[i];
}
dep[1]=1;fa[1]=1;dfs1(1,-1);dfs2(1,1);build(1,1,n);
cin>>q;
for(int i=1;i<=q;++i)
{
int x,y;cin>>ad;cin>>x>>y;
if(ad[1]=='H') update(1,tops[x],y);
if(ad[1]=='M') cout<<pmax(x,y)<<'\n';
if(ad[1]=='S') cout<<psum(x,y)<<'\n';
}
return 0;
}
树的统计
最新推荐文章于 2023-03-02 16:21:48 发布