线段树优化建图学习笔记

线段树优化建图


前置思想

动态开点

示例

操作

有如下 4 操作:

  1. u u u 连到 v v v ,代价为 w w w
  2. u u u 连到 [ l , r ] [l,r] [lr] ,代价为 w w w
  3. [ l , r ] [l,r] [lr] 连到 v v v ,代价为 w w w
  4. [ u , v ] [u,v] [uv] 连到 [ l , r ] [l,r] [lr] ,代价为 w w w
处理
  1. 直接连。
  2. 建立一棵线段树 s e g 1 seg1 seg1,设 p p p 节点表示区间 [ l , r ] [l,r] [lr],将 u u u 连到 p p p 上。
  3. 建立一棵线段树 s e g 2 seg2 seg2,设 p p p 节点表示区间 [ l , r ] [l,r] [lr],将 p p p 连到 v v v 上。
  4. 建立一个虚点 p 0 p0 p0,设 s e g 2 seg2 seg2 p 1 p1 p1 节点表示区间 [ u , v ] [u,v] [uv],设 s e g 1 seg1 seg1 p 2 p2 p2 节点表示区间 [ l , r ] [l,r] [lr],将 p 2 p2 p2 连到 p 0 p0 p0 p 0 p0 p0 连到 p 1 p1 p1
注意事项
  1. 线段树上下层要记得连上,保证正确性。
  2. 空间要足够(图的空间大概是一般线段树的 9 倍,可以减少到 7 倍)。

模版题

题目链接:Legacy - CodeForces 787D.

思路:线段树优化建图,跑dij最短路

int n,Q,s;
struct Chain_Forward_Star{
	struct Edge{
		int v,w,nxt;
	}edge[N*36];
	int h[N*9],cnt;
	void init(){
		cnt=0;RCL(h,-1);
	}
	void add_edge(int u,int v,int w){
		edge[++cnt]={v,w,h[u]};
		h[u]=cnt;
	}
}cfs;
struct Double_Segment_Tree{
	#define ls (p<<1)
	#define rs (p<<1|1)
	int n,tot;
	struct node{
		int l,r,len,t;
	}tr[N<<2][2];
	void init(int _n){
		tot=n=_n;
		build(1,n,1,0),build(1,n,1,1);
	}//初始化
	void build(int l,int r,int p,bool t){
		tr[p][t]={l,r,r-l+1,++tot};
		if(l==r){
			if(!t)cfs.add_edge(tr[p][t].t,l,0);
			else cfs.add_edge(l,tr[p][t].t,0);
			return;
		}
		int mid=l+r>>1;
		build(l,mid,ls,t);
		build(mid+1,r,rs,t);
		if(!t)cfs.add_edge(tr[p][t].t,tr[ls][t].t,0),cfs.add_edge(tr[p][t].t,tr[rs][t].t,0);
		else cfs.add_edge(tr[ls][t].t,tr[p][t].t,0),cfs.add_edge(tr[rs][t].t,tr[p][t].t,0);
	}//建树
	#define mid (tr[p][t].l+tr[p][t].r>>1)
	int l,r,u,w,t;
	void update(int p){
		if(l<=tr[p][t].l&&tr[p][t].r<=r){
			if(!t)cfs.add_edge(u,tr[p][t].t,w);
			else cfs.add_edge(tr[p][t].t,u,w);
			return;
		}
		if(l<=mid)update(ls);
		if(r>mid)update(rs);
	}//加边 
    #undef mid
}seg;

主函数:

signed main(){
	cin>>n>>Q>>s;
	cfs.init(),seg.init(n);
	while(Q--){
		int opt;cin>>opt;
		if(opt==1){
			int u,v,w;cin>>u>>v>>w;
			cfs.add_edge(u,v,w);
		}else{
			int u,l,r,w;cin>>u>>l>>r>>w;
			seg.l=l,seg.r=r,seg.u=u,seg.w=w,seg.t=opt-2;
			seg.update(1);
		}
	}
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值