【洛谷 P2590】 【树剖】 树的统计
题目
解题思路
树剖模板题
用与查询树上连续区间的算法
分为三大步
- 记录父节点,重儿子,深度
void dfs1(ll x,ll fa)
{
f[x]=fa,size[x]=1; //fa记录父节点,size记录子树大小(含自己)
dep[x]=dep[fa]+1; //深度
for (ll i=head[x];i;i=h[i].next)
if (h[i].to!=fa)
{
dfs1(h[i].to,x);
size[x]+=size[h[i].to];
if (size[h[i].to]>size[son[x]]) //求重儿子
son[x]=h[i].to;
}
}
- 重新给编号,求重链
void dfs2(ll x,ll tot)
{
top[x]=tot,id[x]=++now,xh[now]=x; //top记录当前重链的起点,id记录新编号,xh原来的编号
if (!son[x]) return;
dfs2(son[x],tot);
for (ll i=head[x];i;i=h[i].next)
if (h[i].to!=f[x]&&h[i].to!=son[x])
dfs2(h[i].to,h[i].to);
}
- 用线段树修改,查询
每次修改,查询都需要让点处在同一链上
修改,查询时记得要用新编号
代码
#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
const ll inf=10000000;
const ll N=100007;
struct lzf{
ll to,next;
}h[2*N];
struct yty{
int l,r;
}tree[2*N];
string s;
ll n,m,t,x,y,u,v,now;
ll a[N],f[N],xh[N],ma[N],id[N],top[N],dep[N],son[N];
ll size[N],head[N],sum[N];
ll read()
{
ll x=0,f=1;
char c=getchar();
while (c<'0'||c>'9')
{
if (c=='-') f=-f;
c=getchar();
}
while (c>='0'&&c<='9')
{
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
void add(ll x,ll y) //建边
{
h[++t].to=y;
h[t].next=head[x];
head[x]=t;
}
void build(ll k,ll l,ll r) //建线段树
{
tree[k].l=l,tree[k].r=r;
if (l==r)
{
sum[k]=ma[k]=a[xh[l]];
return;
}
ll mid=(l+r)/2;
build(k*2,l,mid);
build(k*2+1,mid+1,r);
sum[k]=sum[k*2]+sum[k*2+1];
ma[k]=max(ma[k*2],ma[k*2+1]);
}
void dfs1(ll x,ll fa) //求重儿子
{
f[x]=fa,size[x]=1;
dep[x]=dep[fa]+1;
for (ll i=head[x];i;i=h[i].next)
if (h[i].to!=fa)
{
dfs1(h[i].to,x);
size[x]+=size[h[i].to];
if (size[h[i].to]>size[son[x]])
son[x]=h[i].to;
}
}
void dfs2(ll x,ll tot) //求重链
{
top[x]=tot,id[x]=++now,xh[now]=x;
if (!son[x]) return;
dfs2(son[x],tot);
for (ll i=head[x];i;i=h[i].next)
if (h[i].to!=f[x]&&h[i].to!=son[x])
dfs2(h[i].to,h[i].to);
}
ll ask(ll k,ll x,ll y) //线段树求和
{
if (tree[k].l>=x&&tree[k].r<=y) return sum[k];
ll mid=(tree[k].l+tree[k].r)/2,res=0;
if (x<=mid) res+=ask(k*2,x,y);
if (y>mid) res+=ask(k*2+1,x,y);
return res;
}
ll qsum(ll x,ll y)
{
ll ans=0;
while (top[x]!=top[y]) //不在同一链上
{
if (dep[top[x]]<dep[top[y]]) swap(x,y); //先跑深度大的
ans+=ask(1,id[top[x]],id[x]);
x=f[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);
ans+=ask(1,id[x],id[y]);
return ans;
}
ll mmax(ll k,ll x,ll y) //线段树求max
{
if (tree[k].l>=x&&tree[k].r<=y) return ma[k];
ll mid=(tree[k].l+tree[k].r)/2,res=-inf;
if (x<=mid) res=max(res,mmax(k*2,x,y));
if (y>mid) res=max(res,mmax(k*2+1,x,y));
return res;
}
ll qmax(ll x,ll y)
{
ll ans=-inf;
while (top[x]!=top[y])
{
if (dep[top[x]]<dep[top[y]]) swap(x,y);
ans=max(ans,mmax(1,id[top[x]],id[x]));
x=f[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);
ans=max(ans,mmax(1,id[x],id[y]));
return ans;
}
void change(ll k,ll x,ll y)
{
if (tree[k].l==tree[k].r)
{
ma[k]=sum[k]=y;
return;
}
ll mid=(tree[k].l+tree[k].r)/2;
if (x<=mid) change(k*2,x,y);
else change(k*2+1,x,y);
sum[k]=sum[k*2]+sum[k*2+1];
ma[k]=max(ma[k*2],ma[k*2+1]);
}
int main()
{
n=read();
for (ll i=1;i<n;i++)
{
x=read(),y=read();
add(x,y);
add(y,x);
}
for (ll i=1;i<=n;i++) a[i]=read();
dfs1(1,0);
dfs2(1,1);
build(1,1,n);
m=read();
for (ll i=1;i<=m;i++)
{
cin>>s;
u=read(),v=read();
if (s=="CHANGE")
change(1,id[u],v);
else if (s=="QMAX")
printf("%lld\n",qmax(u,v));
else printf("%lld\n",qsum(u,v));
}
return 0;
}