Skip to content

BZOJ 1984 月下“毛景树”

约 1405 字大约 5 分钟

图论树链剖分

2018-08-10

题面

题目描述

毛毛虫经过及时的变形,最终逃过的一劫,离开了菜妈的菜园。 毛毛虫经过千山万水,历尽千辛万苦,最后来到了小小的绍兴一中的校园里。爬啊爬爬啊爬毛毛虫爬到了一颗小小的“毛景树”下面,发现树上长着他最爱吃的毛毛果~~ “毛景树”上有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之间树枝上毛毛果个数最多有多少个。

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

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

样例输入

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

样例输出

9
16

提示 n<=100000

题解

这是一道树剖维护边权裸题。和一般的树剖差不多,我们只要将边权放在深度较大的点上,再跑一般的树剖即可。 但是这里有一个很重要的细节: 对于查询(或修改)树上两点之间的路径上的边权时,不能考虑到他们的LCA。因为它们的LCA存的是他到他的父亲的边的权值,而这一条边实际上并不在这两点的路径上。(详细请参考代码注释)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 100005
#define inf 1000000009
using namespace std;
int hd[N],nx[N<<1],to[N<<1],w[N<<1],cnt,u,v,c;
int n,val[N],edge[N],tr[N<<2],isp[N<<2],laz[N<<2];
int fa[N],dep[N],siz[N],son[N],did[N],tot,tid[N],top[N];
inline void add() {
	to[++cnt]=v;
	w[cnt]=c;
	nx[cnt]=hd[u];
	hd[u]=cnt;
	to[++cnt]=u;
	w[cnt]=c;
	nx[cnt]=hd[v];
	hd[v]=cnt;
}
inline void dfs1(int x,int f,int d) {
	fa[x]=f;
	dep[x]=d;
	siz[x]=1;
	for (int i=hd[x];i;i=nx[i]) {
		int t=to[i];
		if(t!=fa[x]) {
			dfs1(t,x,d+1);
			val[t]=w[i];
			edge[i+1>>1]=t;
			siz[x]+=siz[t];
			if(son[x]<0||siz[t]>siz[son[x]]) son[x]=t;
		}
	}
}
inline void dfs2(int x,int s) {
	top[x]=s;
	did[x]=++tot;
	tid[tot]=x;
	if(son[x]<0) return;
	dfs2(son[x],s);
	for (int i=hd[x];i;i=nx[i]) {
		int t=to[i];
		if(t!=son[x]&&t!=fa[x]) dfs2(t,t);
	}
}
inline void pushdown(int id) {
	if(isp[id]) {
		isp[id<<1]=isp[id<<1|1]=isp[id];
		tr[id<<1]=tr[id<<1|1]=isp[id];
		laz[id<<1]=laz[id<<1|1]=isp[id]=0;
	}
	if(laz[id]) {
		laz[id<<1]+=laz[id];
		laz[id<<1|1]+=laz[id];
		tr[id<<1]+=laz[id];
		tr[id<<1|1]+=laz[id];
		laz[id]=0;
	}
}
inline void update(int id) {
	tr[id]=max(tr[id<<1],tr[id<<1|1]);
}
inline void build(int id,int l,int r) {
	if(l==r) {
		tr[id]=val[tid[l]];
		return;
	}
	int mid=l+r>>1;
	build(id<<1,l,mid);
	build(id<<1|1,mid+1,r);
	update(id);
}
inline void interpi(int id,int l,int r,int ll,int rr,int v) {
	if(ll>rr) return;
	if(ll<=l&&rr>=r) {
		tr[id]=v;
		isp[id]=v;
		laz[id]=0;
		return;
	}
	pushdown(id);
	int mid=l+r>>1;
	if(ll<=mid) interpi(id<<1,l,mid,ll,rr,v);
	if(rr>mid) interpi(id<<1|1,mid+1,r,ll,rr,v);
	update(id);
}
inline void interadd(int id,int l,int r,int ll,int rr,int v) {
	if(ll>rr) return;
	if(ll<=l&&rr>=r) {
		tr[id]+=v;
		laz[id]+=v;
		return;
	}
	pushdown(id);
	int mid=l+r>>1;
	if(ll<=mid) interadd(id<<1,l,mid,ll,rr,v);
	if(rr>mid) interadd(id<<1|1,mid+1,r,ll,rr,v);
	update(id);
}
inline int query(int id,int l,int r,int ll,int rr) {
	if(ll>rr) return -inf;
	if(ll<=l&&rr>=r) return tr[id];
	pushdown(id);
	int mid=l+r>>1,ans=-inf;
	if(ll<=mid) ans=max(ans,query(id<<1,l,mid,ll,rr));
	if(rr>mid) ans=max(ans,query(id<<1|1,mid+1,r,ll,rr));
	return ans;
}
inline void ipi(int x,int y,int v) {
	while(top[x]!=top[y]) {
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		interpi(1,1,n,did[top[x]],did[x],v);
		x=fa[top[x]];
	}
	if(did[x]>did[y]) swap(x,y);
	interpi(1,1,n,did[x]+1,did[y],v);
	//这里的did[x]+1就是为了不算上LCA,下面同理
}
inline void iadd(int x,int y,int v) {
	while(top[x]!=top[y]) {
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		interadd(1,1,n,did[top[x]],did[x],v);
		x=fa[top[x]];
	}
	if(did[x]>did[y]) swap(x,y);
	interadd(1,1,n,did[x]+1,did[y],v);
}
inline int q(int x,int y) {
	int ans=-inf;
	while(top[x]!=top[y]) {
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		ans=max(ans,query(1,1,n,did[top[x]],did[x]));
		x=fa[top[x]];
	}
	if(did[x]>did[y]) swap(x,y);
	ans=max(ans,query(1,1,n,did[x]+1,did[y]));
	return ans;
}
int main() {
	memset(son,-1,sizeof son);
	scanf("%d",&n);
	for (int i=1;i<n;i++) {
		scanf("%d%d%d",&u,&v,&c);
		add();
	}
	dfs1(1,0,1);
	dfs2(1,1);
	build(1,1,n);
	while(1) {
		char ch[10];
		int u,v,w;
		scanf(" %s",&ch[0]);
		if(ch[0]=='S') break;
		scanf("%d%d",&u,&v);
		if(ch[1]=='h') interpi(1,1,n,did[edge[u]],did[edge[u]],v); else if(ch[1]=='o') {
			scanf("%d",&w);
			ipi(u,v,w);
		} else if(ch[1]=='d') {
			scanf("%d",&w);
			iadd(u,v,w);
		} else printf("%d\n",q(u,v));
	}
	return 0;
}