1 条题解
-
0
自动搬运
来自洛谷,原作者为

Diu
上分!搬运于
2025-08-24 22:19:56,当前版本为作者最后更新于2021-10-19 21:14:15,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
线段树优化建图
我们可以从最朴素的思路开始想起。
0.0 暴力建图
对于一堆道路 ,两两连边。
1.0 连向公共边
对于一堆道路 ,我们让 连向一个虚拟点 ,
再从 向 连边。
但是这样总共要连 条边,肯定会炸。
所以要想办法优化连边的边数。
2.0 线段树优化建图
这个时候线段树重磅出击。
-
性质 2.1 对于一个区间的点 ,一个包含它的区间 连向的点,那么 也能连向。
-
性质 2.2 对于一个区间的点 ,另一个点 如果能到达一个包含 区间 ,那么点 也能到达 。
由此,我们便可建出两棵线段树,进树和出树。
同时,这也是为什么进树从父节点连向子节点,二出树从子节点连向父节点的原因。
不把两棵树建到一块,是为了防止直接顺着线段树直接走完所有点。
而对于一个区间 它在进树、出树本质上是一个点。
所以经过某条边到达进树后,可以回到出树继续走下一条边。
也就是说进树的点直接连向对应的出树的点。


图中绿色的边权之标上 ,但是最后答案要除以 。
因为从 连到 经过两条绿边,路程算了 ,但其实只用算一条,所以要除以 。
或者连向绿点的边和绿点连出的边任意一个标权值也可以。
code
#include<bits/stdc++.h> #define ls(o) (o<<1) #define rs(o) (o<<1|1) //#define int long long using namespace std; const int N=500010,M=4200010; int n,m,p,out,num[N]; struct edge{ int v,w; }; vector<edge> g[M]; void build_in(int o,int l,int r){ if(l==r)return; int mid=l+r>>1; build_in(ls(o),l,mid); build_in(rs(o),mid+1,r); g[o].push_back({ls(o),0}); g[o].push_back({rs(o),0}); } void build_out(int o,int l,int r){ g[o].push_back({o+n*4,0}); if(l==r)return (void)(num[l]=o+n*4); int mid=l+r>>1; build_out(ls(o),l,mid); build_out(rs(o),mid+1,r); g[ls(o)+n*4].push_back({o+n*4,0}); g[rs(o)+n*4].push_back({o+n*4,0}); } void merge1(int o,int l,int r,int x,int y,int k){ if(l>=x&&r<=y)return (void)(g[k].push_back({o,1}),g[o+n*4].push_back({k+1,1})); int mid=l+r>>1; if(mid>=x)merge1(ls(o),l,mid,x,y,k); if(mid<y)merge1(rs(o),mid+1,r,x,y,k); } void merge2(int o,int l,int r,int x,int y,int k){ if(l>=x&&r<=y)return (void)(g[k+1].push_back({o,1}),g[o+n*4].push_back({k,1})); int mid=l+r>>1; if(mid>=x)merge2(ls(o),l,mid,x,y,k); if(mid<y)merge2(rs(o),mid+1,r,x,y,k); } int d[M]; bool vis[M]; deque<int> q; void dijstra(int s){ memset(d,127,sizeof(d)); d[s]=0; q.push_front(s); while(!q.empty()){ int u=q.front(); q.pop_front(); // if(vis[u])continue; // vis[u]=1; for(int i=0;i<g[u].size();i++){ int v=g[u][i].v; // if(vis[v])continue; int w=g[u][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); } } } } inline char nc(){ static char buf[1000010],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000010,stdin),p1==p2)?EOF:*p1++; } //#define nc getchar inline int read(){ register int s=0,w=0; static char ch=nc(); for(;!isdigit(ch);)ch=nc(); for(;isdigit(ch);){ s=(s<<1)+(s<<3)+(ch^48); ch=nc(); } return w?-s:s; } signed main(){ n=read(),m=read(),p=read(); build_in(1,1,n); build_out(1,1,n); for(register int a,b,c,d,i=1;i<=m;++i){ a=read(),b=read(),c=read(),d=read(); merge1(1,1,n,a,b,n*8+i*2); merge2(1,1,n,c,d,n*8+i*2); } dijstra(num[p]); for(register int i=1;i<=n;++i)printf("%d\n",d[num[i]]/2); // for(int i=1;i<=tot;i++){ // printf("%lld %lld %lld %lld:\n",i,tr[i].l,tr[i].r,g[i].size()); // for(int j=0;j<g[i].size();j++)printf("%lld %lld\n",g[i][j].v,g[i][j].w); // } } -
- 1
信息
- ID
- 5353
- 时间
- 3000ms
- 内存
- 500MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者