正题
题目链接:https://www.luogu.com.cn/problem/P6348
题目大意
nnn个点的一张图,mmm条边表示区间[a,b][a,b][a,b]向区间[c,d][c,d][c,d]连边,求单源最短路。
解题思路
线段树优化建图的裸题,但是不能直接让线段树上的点两两建边,因为这样是log2nlog^2 nlog2n的显然空间顶不住。每条边多建立一个点就好了。
然后最短路可以用双端队列bfsbfsbfs但是这里还是写dijdijdij了,因为双端队列bfsbfsbfs写挂过两次。
时间复杂度O(nlogn)O(n\log n)O(nlogn)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define mp(x,y) make_pair(x,y)
using namespace std;
const int N=4e6+10;
struct node{int to,next,w;
}a[N<<2];
int n,m,s,num,tot,ls[N];
int q[N],p[N],f[N];bool v[N];
void addl(int x,int y,int w){a[++tot].to=y;a[tot].next=ls[x];a[tot].w=w;ls[x]=tot;return;
}
void Build(int x,int l,int r){if(l==r){q[x]=p[x]=l;return;}p[x]=++num;q[x]=++num;int mid=(l+r)>>1;Build(x*2,l,mid);Build(x*2+1,mid+1,r);addl(p[x],p[x*2],0);addl(p[x],p[x*2+1],0);addl(q[x*2],q[x],0);addl(q[x*2+1],q[x],0);return;
}
void Access(int x,int L,int R,int l,int r,int pos){if(L==l&&R==r){if(pos>0)addl(q[x],pos,1);else addl(-pos,p[x],0);return;}int mid=(L+R)>>1;if(r<=mid)Access(x*2,L,mid,l,r,pos);else if(l>mid)Access(x*2+1,mid+1,R,l,r,pos);else Access(x*2,L,mid,l,mid,pos),Access(x*2+1,mid+1,R,mid+1,r,pos);return;
}
void Dij(){memset(f,0x3f,sizeof(f));priority_queue<pair<int,int> >q;q.push(mp(0,s));f[s]=0;while(!q.empty()){int x=q.top().second;q.pop();if(v[x])continue;v[x]=1;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(f[x]+a[i].w<f[y]){f[y]=f[x]+a[i].w;q.push(mp(-f[y],y));}}}return;
}
int main()
{
// printf("%d",sizeof(a)/1024/1024);scanf("%d%d%d",&n,&m,&s);num=n;Build(1,1,n);for(int i=1;i<=m;i++){int a,b,c,d;scanf("%d%d%d%d",&a,&b,&c,&d);++num;Access(1,1,n,a,b,num);Access(1,1,n,c,d,-num);++num;Access(1,1,n,c,d,num);Access(1,1,n,a,b,-num);}Dij();for(int i=1;i<=n;i++)printf("%d\n",f[i]);return 0;
}