[树链剖分,线段树]P1505

本文详细阐述了如何利用树链剖分和线段树的数据结构解决一棵带权重的树的五种操作,包括边权值修改、节点间边权翻转、求和、最大值和最小值查询。作者分享了2小时调试中关于线段树单点修改的错误,并介绍了优化技巧如重载加法运算符。
摘要由CSDN通过智能技术生成

给定一棵 n n n 个节点的树,边带权,需要支持五种操作:

  • 将输入的第 i i i 条边权值改为 w w w
  • u , v u,v u,v 节点之间的边权都变为相反数
  • 询问 u , v u,v u,v 节点之间边权和
  • 询问 u , v u,v u,v 节点之间边权最大值
  • 询问 u , v u,v u,v 节点之间边权最小值

1 ≤ n , m ≤ 2 × 1 0 5 1\le n,m \le 2\times 10^5 1n,m2×105


调了2个多小时的题,纪念一下。树链剖分基本上都是同一个套路,所以就拿这一题详细写一下。

首先用2次 D F S DFS DFS 得到分解成链后的(在处理的时候用儿子点存起来比较方便),之后建一个线段树维护。单点修改直接处理省去一个 t a g tag tag ,只需要存储一个 t a g tag tag 来表示是否有需要处理的相反数操作。

在遇到相反数操作时, m a x = − m i n max=-min max=min m i n = − m a x min=-max min=max s u m = − s u m sum=-sum sum=sum 即可。对 t a g tag tag 异或1即可。

最后写一下调了2个多小时的原因,在线段树单点修改时递归参数给错了,看了几遍没看出来,血的教训!

顺便说一下,对于复杂信息上传其实可以用重载加法运算符(这里没用),这样可以省去一些代码量

#include<bits/stdc++.h>
#define ll long long
#define FOR(i,j,k) for(int i=j;i<=k;++i)
#define pb push_back

ll in(){
	ll x=0,f=1;
	char c;
	do{
		c=getchar();
		if(c=='-'){
			f=-1;
		}
	}while(c<'0' || c>'9');
	while(c>='0' && c<='9'){
		x=(x<<3)+(x<<1)+c-'0';
		c=getchar();
	}
	return f*x;
}

struct ST_node{
	int minn,maxn,sum;
	int l,r,tag;
};

struct ST{
	ST_node nd[800005];
	int a[200005];
	#define ls(p) p<<1
	#define rs(p) p<<1|1
	#define lr int l=nd[p].l;int r=nd[p].r
	inline void add_tag(int p){
		nd[p].tag=1-nd[p].tag;
		std::swap(nd[p].maxn,nd[p].minn);
		nd[p].maxn=-nd[p].maxn;
		nd[p].minn=-nd[p].minn;
		nd[p].sum=-nd[p].sum;
	}
	inline void push_up(int p){
		nd[p].maxn=std::max(nd[ls(p)].maxn,nd[rs(p)].maxn);
		nd[p].minn=std::min(nd[ls(p)].minn,nd[rs(p)].minn);
		nd[p].sum=nd[ls(p)].sum+nd[rs(p)].sum;
	}
	void push_down(int p){
		if(nd[p].tag){
			add_tag(ls(p));
			add_tag(rs(p));
			nd[p].tag=0;
		}
	}
	void build(int l,int r,int p){
		nd[p].l=l;
		nd[p].r=r;
		if(l==r){
			nd[p].maxn=nd[p].minn=nd[p].sum=a[l];
			return;
		}
		int mid=(l+r)>>1;
		build(l,mid,ls(p));
		build(mid+1,r,rs(p));
		push_up(p);
	}
	void change(int p,int q,int k){
		lr;
		if(l==r){
			nd[p].maxn=k;nd[p].minn=k;nd[p].sum=k;
			return;
		}
		push_down(p);
		int mid=(l+r)>>1;
		if(q<=mid)
			change(ls(p),q,k);
		if(q>mid)
			change(rs(p),q,k);
		push_up(p);
	}
	void reverse(int p,int ql,int qr){
		lr;
		if(ql<=l && r<=qr){
			add_tag(p);
			return;
		}
		push_down(p); 
		int mid=(l+r)>>1;
		if(ql<=mid)
			reverse(ls(p),ql,qr);
		if(qr>mid)
			reverse(rs(p),ql,qr);
		push_up(p);
	}
	ST_node get_all(int p,int ql,int qr){
		lr;
		if(ql<=l && r<=qr){
			return nd[p];
		}
		int mid=(l+r)>>1;
		push_down(p);
		ST_node gl,gr;
		if(ql<=mid)
			gl=get_all(ls(p),ql,qr);
		else if(qr>mid)
			return get_all(rs(p),ql,qr);
		if(qr>mid)
			gr=get_all(rs(p),ql,qr);
		else
			return gl;
		gl.sum+=gr.sum;
		gl.maxn=std::max(gl.maxn,gr.maxn);
		gl.minn=std::min(gl.minn,gr.minn);
		return gl;
	}
}S;

struct Tree{
	struct Tree_node{
		int fa,son,to,top,sz,dep,val;
		std::vector <int> v,w,id;
	}nd[200005];
	int b[200005],cnt;
	void DFS1(int x){
		nd[x].dep=nd[nd[x].fa].dep+1;
		nd[x].sz=1;
		int mx=-1;
		for(int i=0;i<nd[x].v.size();++i){
			int y=nd[x].v[i];
			if(y==nd[x].fa)
				continue;
			nd[y].fa=x;
			nd[y].val=nd[x].w[i];
			b[nd[x].id[i]]=y;
			DFS1(y);
			nd[x].sz+=nd[y].sz;
			if(nd[y].sz>mx){
				mx=nd[y].sz;
				nd[x].son=y;
			}
		}
	}
	void DFS2(int x,int top){
		cnt++;
		nd[x].top=top;
		nd[x].to=cnt;
		S.a[cnt]=nd[x].val;
		if(nd[x].son)
			DFS2(nd[x].son,top);
		for(int i=0;i<nd[x].v.size();++i){
			int y=nd[x].v[i];
			if(y==nd[x].fa || y==nd[x].son)
				continue;
			DFS2(y,y);
		}
	}
	void Rev(int x,int y){
		while(nd[x].top!=nd[y].top){
			if(nd[nd[x].top].dep<nd[nd[y].top].dep)
				std::swap(x,y);
			S.reverse(1,nd[nd[x].top].to,nd[x].to);
			x=nd[nd[x].top].fa;
		}
		if(nd[x].dep>nd[y].dep)
			std::swap(x,y);
		if(x!=y){
			S.reverse(1,nd[x].to+1,nd[y].to);
		}
			
	}
	ST_node Query(int x,int y){
		ST_node ret;
		ret.maxn=-100000000;
		ret.minn=100000000;
		ret.sum=0;
		while(nd[x].top!=nd[y].top){
			if(nd[nd[x].top].dep<nd[nd[y].top].dep)
				std::swap(x,y);
			ST_node st_node=S.get_all(1,nd[nd[x].top].to,nd[x].to);
			ret.maxn=std::max(ret.maxn,st_node.maxn);
			ret.minn=std::min(ret.minn,st_node.minn);
			ret.sum+=st_node.sum;
			x=nd[nd[x].top].fa;
		}
		if(nd[x].dep>nd[y].dep)
			std::swap(x,y);
		if(x!=y){
			ST_node st_node=S.get_all(1,nd[x].to+1,nd[y].to);
			ret.maxn=std::max(ret.maxn,st_node.maxn);
			ret.minn=std::min(ret.minn,st_node.minn);
			ret.sum+=st_node.sum;
		}
		return ret;
	}
}T;

int n,m;

int main(){
	n=in();
	FOR(i,1,n-1){
		int u=in(),v=in(),w=in();
		T.nd[u].id.pb(i);	T.nd[v].id.pb(i);
		T.nd[u].v.pb(v);	T.nd[v].v.pb(u);
		T.nd[u].w.pb(w);	T.nd[v].w.pb(w);
	}
	T.cnt=-1;
	T.DFS1(0);
	T.DFS2(0,0);
	S.build(1,n-1,1);
	m=in();
	while(m){
		m--;
		char c;
		do{
			c=getchar();
		}while(c!='S' && c!='M' && c!='N' && c!='C');
		switch(c){
			case('C'):{
				int i=in(),w=in();
				S.change(1,T.nd[T.b[i]].to,w);
				break;
			}
			case('N'):{
				int u=in(),v=in();
				T.Rev(u,v);
				break;
			}
			case('S'):{
				int u=in(),v=in();
				printf("%d\n",T.Query(u,v).sum);
				break;
			}
			case('M'):{
				if(getchar()=='A'){
					int u=in(),v=in();
					printf("%d\n",T.Query(u,v).maxn);
				}
				else{
					int u=in(),v=in();
					printf("%d\n",T.Query(u,v).minn);
				}
				break;
			}
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值