线段树优化建图学习笔记
线段树优化建图
起因
一般情况下,连边发生在两个点之间。
但在某些题目中,要在点和区间之间连边。
所以有了线段树优化建图。
原理
从模板题看起。
显然,直接暴力连边的最坏时间复杂度和空间复杂度都为 。
线段树建图的原理就是建出线段树,区间连边转化为向线段树上的节点连边。
但只是如图所示的连边是不够的,对于区间中的所有点都要连边。
所以父子节点之间连边权为 的边就能解决问题了。
至于连边的方向由要求决定。
- 如果要求点向区间连边,线段树上就从父节点向子节点连边,目标点向线段树上节点连边。
- 如果要求区间向点连边,线段树上就从子节点向父节点连边,线段树上节点向目标点连边。
还有一个小细节点:编号问题。
如果直接按照朴素方法建出线段树,则树上节点的编号可能会与题目中的节点编号产生冲突。
解决方法是,利用类似于动态开点线段树的写法,每个线段树节点从 开始分配,其中叶子节点的编号就是区间本身,也就省去了某些写法要在两棵线段树的叶子节点之间连边的操作。
建树的代码:
void b1(int &p,int l,int r){if(l==r){p=l;return ;}p=++tot;MID b1(ls[p],l,m);b1(rs[p],m+1,r);I(p,ls[p]),I(p,rs[p]);}
void b2(int &p,int l,int r){if(l==r){p=l;return ;}p=++tot;MID b2(ls[p],l,m);b2(rs[p],m+1,r);I(ls[p],p),I(rs[p],p);}
看回到题目,题目要求点之间连边,点向区间连边,区间向点连边,求最短路。
建出两棵线段树,然后按要求连边即可。
#include <bits/stdc++.h>
#define MID int m=(l+r)>>1;
using namespace std;const int N=8e6;int rd(){
int w=0,v=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')v=-1;c=getchar();}
while(c>='0'&&c<='9')w=(w<<1)+(w<<3)+(c&15),c=getchar();return w*v;
}void wr(int x){if(x<0)putchar('-'),x=-x;if(x>9)wr(x/10);putchar(x%10+'0');}
int fir[N],c,cnt,ls[N],rs[N],n,m,p,r1,r2,xd,vis[N],d[N];deque <int>q;struct E{int v,nt,w;}e[N];
void I(int u,int v,int w=0){e[++cnt]=(E){v,fir[u],w};fir[u]=cnt;}
void b1(int &p,int l,int r){if(l==r){p=l;return;}p=++c;MID b1(ls[p],l,m);b1(rs[p],m+1,r);I(p,ls[p]);I(p,rs[p]);}
void b2(int &p,int l,int r){if(l==r){p=l;return;}p=++c;MID b2(ls[p],l,m);b2(rs[p],m+1,r);I(ls[p],p);I(rs[p],p);}
void ch(int p,int l,int r,int L,int R,bool f){
if(l>=L&&r<=R){if(f)I(p,xd,1);else I(xd,p,1);return ;}
MID if(L<=m)ch(ls[p],l,m,L,R,f);if(R>m)ch(rs[p],m+1,r,L,R,f);return ;
}void dj(int p){
memset(d,0x7f,sizeof(d));d[p]=0;q.push_front(p);while(!q.empty()){
int u=q.front(),v,w;q.pop_front();
for(int i=fir[u];i;i=e[i].nt){
v=e[i].v,w=e[i].w;if(d[v]>d[u]+w){d[v]=d[u]+w;if(w==1)q.push_back(v);else q.push_front(v);}
}
}
}signed main(){
n=rd(),m=rd(),p=rd();c=n;b1(r1,1,n),b2(r2,1,n);xd=c+1;for(int i=1,a,b,c,d;i<=m;i++)
a=rd(),b=rd(),c=rd(),d=rd(),ch(r2,1,n,a,b,1),ch(r1,1,n,c,d,0),xd++,ch(r2,1,n,c,d,1),ch(r1,1,n,a,b,0),xd++;dj(p);
for(int i=1;i<=n;i++)wr(d[i]/2),putchar('\n');return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!