【洛谷 P2590】 【树剖】 树的统计

【洛谷 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值