bzoj 1984: 月下“毛景树” (树链剖分)

本文介绍了一道关于树链剖分的经典算法题,详细解释了如何通过树链剖分来高效处理节点间路径更新与查询的问题。文章通过具体实例展示了如何实现节点更新、区间覆盖、区间增加及最大值查询等操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1984: 月下“毛景树”

Time Limit: 20 Sec   Memory Limit: 64 MB
Submit: 1667   Solved: 514
[ Submit][ Status][ Discuss]

Description

毛毛虫经过及时的变形,最终逃过的一劫,离开了菜妈的菜园。 毛毛虫经过千山万水,历尽千辛万苦,最后来到了小小的绍兴一中的校园里。爬啊爬~爬啊爬~~毛毛虫爬到了一颗小小的“毛景树”下面,发现树上长着他最爱吃的毛毛果~~~ “毛景树”上有N个节点和N-1条树枝,但节点上是没有毛毛果的,毛毛果都是长在树枝上的。但是这棵“毛景树”有着神奇的魔力,他能改变树枝上毛毛果的个数:  Change k w:将第k条树枝上毛毛果的个数改变为w个。  Cover u v w:将节点u与节点v之间的树枝上毛毛果的个数都改变为w个。  Add u v w:将节点u与节点v之间的树枝上毛毛果的个数都增加w个。 由于毛毛虫很贪,于是他会有如下询问:  Max u v:询问节点u与节点v之间树枝上毛毛果个数最多有多少个。

Input

第一行一个正整数N。 接下来N-1行,每行三个正整数Ui,Vi和Wi,第i+1行描述第i条树枝。表示第i条树枝连接节点Ui和节点Vi,树枝上有Wi个毛毛果。 接下来是操作和询问,以“Stop”结束。

Output

对于毛毛虫的每个询问操作,输出一个答案。

Sample Input

4
1 2 8
1 3 7
3 4 9
Max 2 4
Cover 2 4 5
Add 1 4 10
Change 1 16
Max 2 4
Stop

Sample Output

9
16

【Data Range】
1<=N<=100,000,操作+询问数目不超过100,000。
保证在任意时刻,所有树枝上毛毛果的个数都不会超过10^9个。

HINT

Source

[ Submit][ Status][ Discuss]

题解:树链剖分。
这个题的关键在于两个标记的处理,注意增加标记和覆盖标记不能同时存在,如果打增加标记的时候,存在覆盖标记,那么就把增加量直接加给覆盖标记。如果打覆盖标记的时候有增加标记,就将增加标记清零。在标记下放的时候也要这么做。
//bzoj 1984
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 200003
using namespace std;
int tr[N*4],n,m,cover[N*4],delta[N*4],pos[N],a[N],deep[N],tot,sz,num[N],q[N];
int point[N],next[N],v[N],c[N],val[N],belong[N],size[N],son[N],fa[N],ba[N];
void add(int x,int y,int z,int i)
{
	tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z; num[tot]=i;
	tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z; num[tot]=i;
}
void dfs(int x,int f)
{
	fa[x]=f; size[x]=1; deep[x]=deep[f]+1;
	for (int i=point[x];i;i=next[i]) 
	 if (v[i]!=f) {
	 	dfs(v[i],x); val[v[i]]=c[i]; ba[num[i]]=v[i];
	 	size[x]+=size[v[i]];
	 	if (size[son[x]]<size[v[i]]) son[x]=v[i];
	 }
}
void dfs1(int x,int chain)
{
	belong[x]=chain; pos[x]=++sz; q[sz]=x;
	if (!son[x]) return;
	dfs1(son[x],chain);
	for (int i=point[x];i;i=next[i])
	 if (v[i]!=son[x]&&v[i]!=fa[x])
	  dfs1(v[i],v[i]);
}
void update(int now)
{
	tr[now]=max(tr[now<<1],tr[now<<1|1]);
}
void pushdown(int now)
{
	if (cover[now]!=-1) {
		tr[now<<1]=cover[now]; tr[now<<1|1]=cover[now];
		cover[now<<1]=cover[now<<1|1]=cover[now];
		cover[now]=-1; delta[now<<1]=delta[now<<1|1]=0;
	}
	if (delta[now]) {
		tr[now<<1]+=delta[now]; tr[now<<1|1]+=delta[now];
		if (cover[now<<1]==-1) delta[now<<1]+=delta[now];
		else cover[now<<1]+=delta[now];
		if (cover[now<<1|1]==-1) delta[now<<1|1]+=delta[now];
		else cover[now<<1|1]+=delta[now];
		delta[now]=0;
	}
}
void build(int now,int l,int r)
{
	if (l==r) {
		tr[now]=val[q[l]];
		return;
	}
	int mid=(l+r)/2;
	build(now<<1,l,mid);
	build(now<<1|1,mid+1,r);
	update(now);
}
void pointchange(int now,int l,int r,int x,int v)
{
	if (l==r) {
		tr[now]=v;
		return;
	}
	int mid=(l+r)/2;
	pushdown(now);
	if (x<=mid) pointchange(now<<1,l,mid,x,v);
	else pointchange(now<<1|1,mid+1,r,x,v);
	update(now);
}
void qjchange(int now,int l,int r,int ll,int rr,int v)
{
	if (ll<=l&&r<=rr) {
		tr[now]+=v;
		if (cover[now]!=-1) cover[now]+=v;
		else delta[now]+=v;
		return;
	}
	int mid=(l+r)/2;
	pushdown(now);
	if (ll<=mid) qjchange(now<<1,l,mid,ll,rr,v);
	if (rr>mid) qjchange(now<<1|1,mid+1,r,ll,rr,v);
	update(now);
}
void qjcover(int now,int l,int r,int ll,int rr,int v)
{
	if (ll<=l&&r<=rr) {
		tr[now]=v;
		delta[now]=0;
		cover[now]=v;
		return;
	}
	int mid=(l+r)/2;
	pushdown(now);
	if (ll<=mid) qjcover(now<<1,l,mid,ll,rr,v);
	if (rr>mid) qjcover(now<<1|1,mid+1,r,ll,rr,v);
	update(now);
}
int qjmax(int now,int l,int r,int ll,int rr)
{
	if (ll<=l&&r<=rr) return tr[now];
	int mid=(l+r)/2;
	pushdown(now);
	int ans=0;
	if (ll<=mid) ans=max(ans,qjmax(now<<1,l,mid,ll,rr));
	if (rr>mid) ans=max(ans,qjmax(now<<1|1,mid+1,r,ll,rr));
	return ans;
}
void solve(int x,int y,int v,int opt)
{
	while (belong[x]!=belong[y]) {
		if (deep[belong[x]]<deep[belong[y]]) swap(x,y);
		if (!opt) qjchange(1,1,n,pos[belong[x]],pos[x],v);
		else qjcover(1,1,n,pos[belong[x]],pos[x],v);
		x=fa[belong[x]];
	}
	if (x==y) return ;
	if (deep[x]>deep[y]) swap(x,y);
	if (!opt) qjchange(1,1,n,pos[x]+1,pos[y],v);
	else qjcover(1,1,n,pos[x]+1,pos[y],v);
}
int solve1(int x,int y)
{
	int ans=0;
	while (belong[x]!=belong[y]){
		if (deep[belong[x]]<deep[belong[y]]) swap(x,y);
		ans=max(ans,qjmax(1,1,n,pos[belong[x]],pos[x]));
		x=fa[belong[x]];
	}
	if (deep[x]>deep[y]) swap(x,y);
	if (x==y) return ans;
	ans=max(ans,qjmax(1,1,n,pos[x]+1,pos[y]));
	return ans;
}
int main()
{
	freopen("ok.in","r",stdin);
	freopen("my.out","w",stdout);
	scanf("%d",&n);
	for (int i=1;i<n;i++) {
		int x,y,z; scanf("%d%d%d",&x,&y,&z);
		add(x,y,z,i);
	}
	memset(cover,-1,sizeof(cover));
	dfs(1,0); 
	dfs1(1,1);
	//for (int i=1;i<=n;i++) cout<<val[i]<<" "; cout<<endl;
	//for (int i=1;i<=n;i++) cout<<q[i]<<" "; cout<<endl;
	build(1,1,n);
	while(true){
		char s[10]; scanf("%s",s+1);
		if (s[1]=='S') break;
		if (s[1]=='C'&&s[2]=='h') {
			int x,y; scanf("%d%d",&x,&y);
			pointchange(1,1,n,pos[ba[x]],y);
		}
		if (s[1]=='C'&&s[2]=='o') {
			int x,y,z; scanf("%d%d%d",&x,&y,&z);
			solve(x,y,z,1);
		}
		if (s[1]=='A') {
			int x,y,z; scanf("%d%d%d",&x,&y,&z);
			solve(x,y,z,0);
		}
		if (s[1]=='M') {
			int x,y; scanf("%d%d",&x,&y);
			printf("%d\n",solve1(x,y));
		}
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值