专栏文章

题解 P6348 [PA2011]Journeys

P6348题解参与者 17已保存评论 20

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
20 条
当前快照
1 份
快照标识符
@mk262qs1
此快照首次捕获于
2026/01/06 13:46
上个月
此快照最后确认于
2026/01/06 13:46
上个月
查看原文

线段树优化建图

我们可以从最朴素的思路开始想起。

0.0 暴力建图

对于一堆道路 (a,b),(c,d)(a,b),(c,d),两两连边。

1.0 连向公共边

对于一堆道路 (a,b),(c,d)(a,b),(c,d),我们让 (a,b)(a,b) 连向一个虚拟点 kk
再从 kk(c,d)(c,d) 连边。
但是这样总共要连 2nm2nm 条边,肯定会炸。
所以要想办法优化连边的边数。

2.0 线段树优化建图

这个时候线段树重磅出击。
  • 性质 2.1 对于一个区间的点 (l,r)(l,r),一个包含它的区间 (L,R)(L,R) 连向的点,那么 (l,r)(l,r) 也能连向。
  • 性质 2.2 对于一个区间的点 (l,r)(l,r),另一个点 aa 如果能到达一个包含 (l,r)(l,r) 区间 (L,R)(L,R),那么点 aa 也能到达 (l,r)(l,r)
由此,我们便可建出两棵线段树,进树和出树。
同时,这也是为什么进树从父节点连向子节点,二出树从子节点连向父节点的原因。
不把两棵树建到一块,是为了防止直接顺着线段树直接走完所有点。
而对于一个区间 (l,r)(l,r) 它在进树、出树本质上是一个点。
所以经过某条边到达进树后,可以回到出树继续走下一条边
也就是说进树的点直接连向对应的出树的点。
图中绿色的边权之标上 11,但是最后答案要除以 22
因为从 (a,b)(a,b) 连到 (c,d)(c,d) 经过两条绿边,路程算了 22,但其实只用算一条,所以要除以 22
或者连向绿点的边和绿点连出的边任意一个标权值也可以。

code

CPP
#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);
//  }
}

评论

20 条评论,欢迎与作者交流。

正在加载评论...