P2590 [ZJOI2008]树的统计

Description

给定一棵 n ( n ≤ 3 × 1 0 4 ) n(n\leq 3\times 10^4) n(n3×104)个节点的带点权的树, q ( q ≤ 2 × 1 0 5 ) q(q\leq 2\times 10^5) q(q2×105)次操作,操作包括单点权值修改、询问两点间路径节点最大权值和两点间路径所有节点权值和。

Solution

这是一个裸的轻重链剖分题,此处不再陈述算法流程,注意三点细节:

1、 r k rk rk数组的作用—建线段树时子节点 p p p的权值 = a [ r k [ l [ p ] ] ] =a[rk[l[p]]] =a[rk[l[p]]] r k [ i ] : d f s rk[i]:dfs rk[i]:dfs序为 i i i的节点的序号);

2、时刻注意节点下标与 d f s dfs dfs序对应节点下标的转换;

3、比较最大值:由于点权可能为负,故需注意某些不应初始化为0的变量。

Code

#include<cstdio>
#include<iostream>
#include<string>
#define ri register int
using namespace std;

const int MAXN=3e4+20;
int N,Q,M,a[MAXN],u[MAXN<<1],v[MAXN<<1],fst[MAXN<<1],nxt[MAXN<<1],ui,vi;
int dep[MAXN],fa[MAXN],siz[MAXN],valt[MAXN],son[MAXN],top[MAXN],dfn[MAXN],rk[MAXN],cnt;
int l[MAXN<<2],r[MAXN<<2],sum[MAXN<<2],maxn[MAXN<<2];
string opt;

void dfs1(int x,int father)
{
	fa[x]=father,dep[x]=dep[fa[x]]+1,siz[x]=1;
	for(ri k=fst[x];k>0;k=nxt[k])
		if(v[k]!=fa[x])
		{
			dfs1(v[k],x);
			siz[x]+=siz[v[k]];
			if(siz[v[k]]>valt[x])
				valt[x]=siz[v[k]],son[x]=v[k];
		}
}

void dfs2(int x,int anc)
{
	top[x]=anc,dfn[x]=++cnt; rk[dfn[x]]=x;
	if(son[x]!=0)	dfs2(son[x],anc);
	for(ri k=fst[x];k>0;k=nxt[k])
		if((v[k]!=fa[x])&&(v[k]!=son[x]))	dfs2(v[k],v[k]);
}

void pushup(int p)
{
	sum[p]=sum[p <<1]+sum[p <<1|1];
	maxn[p]=max(maxn[p <<1],maxn[p <<1|1]);
}

void build(int p,int lft,int rit)
{
	l[p]=lft,r[p]=rit;
	if(l[p]==r[p])
	{
		sum[p]=a[rk[l[p]]],maxn[p]=sum[p];
		return;
	}
	ri mid=(lft+rit)>>1;
	build(p <<1,lft,mid); build(p <<1|1,mid+1,rit);
	pushup(p);
}

void update(int p,int pla,int k)
{
	if((l[p]==pla)&&(r[p]==pla))
	{
		sum[p]=k,maxn[p]=k;
		return;
	}
	if(pla<=r[p <<1]) update(p <<1,pla,k);
	if(l[p <<1|1]<=pla) update(p <<1|1,pla,k);
	pushup(p);
}

int querymax(int p,int lft,int rit)
{
	if(lft<=l[p]&&r[p]<=rit) return maxn[p];
	int ans=-1e9;
	if(lft<=r[p <<1]) ans=max(ans,querymax(p <<1,lft,rit));
	if(l[p <<1|1]<=rit) ans=max(ans,querymax(p <<1|1,lft,rit));
	return ans;
}

int LCAqmax(int x,int y)
{
	int ans=-1e9;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]])	swap(x,y);
		ans=max(ans,querymax(1,dfn[top[x]],dfn[x]));
		x=fa[top[x]];
	}
	if(dep[x]>dep[y])	swap(x,y);
	ans=max(ans,querymax(1,dfn[x],dfn[y]));
	return ans;
}

int querysum(int p,int lft,int rit)
{
	if(lft<=l[p]&&r[p]<=rit) return sum[p];
	int ans=0;
	if(lft<=r[p <<1]) ans=querysum(p <<1,lft,rit);
	if(l[p <<1|1]<=rit) ans+=querysum(p <<1|1,lft,rit);
	return ans;
}

int LCAqsum(int x,int y)
{
	int ans=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]])	swap(x,y);
		ans+=querysum(1,dfn[top[x]],dfn[x]);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y])	swap(x,y);
	ans+=querysum(1,dfn[x],dfn[y]);
	return ans;
}

int main()
{
	std::ios::sync_with_stdio(false);
	cin>>N;
	M=(N-1)<<1;
	for(ri i=1;i<=M;i+=2)
	{
		cin>>u[i]>>v[i];
		nxt[i]=fst[u[i]],fst[u[i]]=i;
		u[i+1]=v[i],v[i+1]=u[i];
		nxt[i+1]=fst[u[i+1]],fst[u[i+1]]=i+1;
	}
	for(ri i=1;i<=N;++i) cin>>a[i];
	dep[0]=-1;
	dfs1(1,0);
	dfs2(1,1);
	build(1,1,N);
	cin>>Q;
	for(ri op=1;op<=Q;++op)
	{
		cin>>opt>>ui>>vi;
		if(opt=="CHANGE")
			update(1,dfn[ui],vi);
		if(opt=="QMAX")
			cout<<LCAqmax(ui,vi)<<'\n';			
		if(opt=="QSUM")
			cout<<LCAqsum(ui,vi)<<'\n';	
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值