线段树优化建图
前置思想
动态开点。
示例
操作
有如下 4 操作:
- 将 u u u 连到 v v v ,代价为 w w w。
- 将 u u u 连到 [ l , r ] [l,r] [l,r] ,代价为 w w w。
- 将 [ l , r ] [l,r] [l,r] 连到 v v v ,代价为 w w w。
- 将 [ u , v ] [u,v] [u,v] 连到 [ l , r ] [l,r] [l,r] ,代价为 w w w。
处理
- 直接连。
- 建立一棵线段树 s e g 1 seg1 seg1,设 p p p 节点表示区间 [ l , r ] [l,r] [l,r],将 u u u 连到 p p p 上。
- 建立一棵线段树 s e g 2 seg2 seg2,设 p p p 节点表示区间 [ l , r ] [l,r] [l,r],将 p p p 连到 v v v 上。
- 建立一个虚点 p 0 p0 p0,设 s e g 2 seg2 seg2 上 p 1 p1 p1 节点表示区间 [ u , v ] [u,v] [u,v],设 s e g 1 seg1 seg1 上 p 2 p2 p2 节点表示区间 [ l , r ] [l,r] [l,r],将 p 2 p2 p2 连到 p 0 p0 p0, p 0 p0 p0 连到 p 1 p1 p1。
注意事项
- 线段树上下层要记得连上,保证正确性。
- 空间要足够(图的空间大概是一般线段树的 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;
}