题意:
就是给定一张n nn个点的图,求源点s ss到每个点的单源最短路。
这张图共有q组边,连边方式有3种:
a→b ,边权为w的单向边;
a→[l,r] ,即a到连续区间[l,r]中的每一个点都有一条边权为w的边。
[l,r]→a,即连续区间[l,r] 中的每一个点都有一条到a 边权为w 的边。
注意数据范围n,q≤10^5.
题解
如果暴力连边(将一对多或者多对一的连边拆开分别连边),那么最差情况下,我们的边数将达到O(nq) 级别,连边就直接超出能承受的范围——更不要说再跑最短路了。因此我们考虑如何使用一些数据结构把这些一对多的情况压缩成少数的一条边,使每一个连边操作都是O(1) O(1)的。
注意到所有“一对多”和“多对一“中“多”都是连续区间,回想我们平时处理连续区间的时候,最经常采用的一个数据结构就是线段树了。于是我们把线段树抓两棵过来放在那边。
那我们怎么用线段树呢?
我们在做区间加法的时候,遇到区间问题,我们都是往下查点,直到以这个点为根节点的子树完全包含于我们需要的区间内的时候,直接返回这个点的值——那么我们也可以采用同样的思想:如果我们把线段树的叶节点当做是原图的点,那么对于以这个点为根节点的子树的所有叶节点如果都在范围内,那么我们是不是可以直接在这个子树的根节点的地方连边呢?这样,对于这样一个子区间,我们只需要1条边。至于我们给出的一段区间会被拆成几个小区间,根据线段树的基本知识,我们知道它是常数级别的,因此我们连边就全部变成了O(1)的。
慢着,我们刚才说连边连在了线段树的中间,可是原图的点是叶节点,
那么我们怎么才能走到中间的那些点呢?
于是我们就只好将每一个点向父节点连边,边权为0。这样就解决问题了?
并没有。因为我们连完边要跑最短路啊,如果我们都向父节点连边,那么中间代表区间的点又应该连向哪里呢?难道连向叶节点吗?这样只能解决多对一的问题,而一对多却不能解决。同样,如果由父节点连向儿子结点,也是不对的。
那么我们就开2 棵线段树吧!我们令一颗叫做“入”树(intree),一棵叫做“出”树(outree),其中intree内部从下往上连0权边,outree内部从上往下连边0权边,那么我们对应的连边操作就是:
对于第一种,我们直接将intree对应u的叶节点连向outree对应v的点。
对于第二种,我们将intree对应a的叶节点连向一个或少数几个outree对应的代表区间的结点。
对于第三种,我们将一个或少数几个intree对应的代表区间的结点连向outree对应a的叶节点。
特别的,因为我们对于每个点在intree和outree都有直接对应的结点,他们实际上表示的是同一个东西,所以我们在intree和outree的叶节点之间对应的连上无向边。
最后,我们只需要跑一遍从intree的对应s结点跑一遍到outree对应所有结点的最短路即可。
时间复杂度:O((n+q)log2n+最短路时间复杂度)
?
空间复杂度:O(n) 。
(简单来说就是将对应区间(建图)内的点的权值赋为0,以区间对应的线段树上的结点作为一个点与对应点建图(区间对应的根结点与点建图),然后就是跑一遍最短路就行)
C++代码
#include<cstdio> #include<cstring> #include<queue> using namespace std; typedef long long ll; #define lson(x) ((x)<<1) #define rson(x) ((x)<<1|1) const ll MAXN=100010; const ll MAXM=1000010; long long INF; struct node{//segment_tree ll id; ll l,r; }intree[MAXN<<2],outree[MAXN<<2]; //两种写法均可 //struct edge{//graph::edge // ll v; // long long w; // edge *nxt; // edge(){ // v=w=0; // nxt=NULL; // } //}*head[MAXM]; struct edge{ ll v; long long w; ll nxt; }e[MAXM<<2]; //记得开大,不然re struct road{//dijkstra ll i; long long dis; bool operator<(const road &rhs)const{ return dis>rhs.dis; } }; priority_queue<road>q; ll n,m,s; ll innum[MAXN],ounum[MAXN]; long long d[MAXM]; ll vis[MAXM],cnt=0; ll tot = 0; ll head[MAXM<<2]; void adde(ll u,ll v,long long w){ // edge *p=new edge; // p->v=v;p->w=w; // p->nxt=head[u]; // head[u]=p; tot++; e[tot].v = v; e[tot].w = w; e[tot].nxt = head[u]; head[u] = tot; } ll inbuild(ll o,ll l,ll r){ intree[o].l=l;intree[o].r=r;intree[o].id=cnt++; if(l==r) return innum[l]=intree[o].id; ll mid=(l+r)>>1; adde(inbuild(lson(o),l,mid),intree[o].id,0); adde(inbuild(rson(o),mid+1,r),intree[o].id,0); return intree[o].id; } ll oubuild(ll o,ll l,ll r){ outree[o].l=l;outree[o].r=r;outree[o].id=cnt++; if(l==r) return ounum[l]=outree[o].id; ll mid=(l+r)>>1; adde(outree[o].id,oubuild(lson(o),l,mid),0); adde(outree[o].id,oubuild(rson(o),mid+1,r),0); return outree[o].id; } void inmodify(ll o,ll l,ll r,ll fa,long long w){ if(intree[o].l>=l&&intree[o].r<=r){ adde(intree[o].id,fa,w); return; } ll mid=(intree[o].l+intree[o].r)>>1; if(r<=mid) inmodify(lson(o),l,r,fa,w); else if(l>=mid+1) inmodify(rson(o),l,r,fa,w); else{ inmodify(lson(o),l,mid,fa,w); inmodify(rson(o),mid+1,r,fa,w); } } void oumodify(ll o,ll l,ll r,ll fa,long long w){ if(outree[o].l>=l&&outree[o].r<=r){ adde(fa,outree[o].id,w); return; } ll mid=(outree[o].l+outree[o].r)>>1; if(r<=mid) oumodify(lson(o),l,r,fa,w); else if(l>=mid+1) oumodify(rson(o),l,r,fa,w); else{ oumodify(lson(o),l,mid,fa,w); oumodify(rson(o),mid+1,r,fa,w); } } void sssp(){ memset(d,0x7f,sizeof(d));INF=d[0]; q.push((road){innum[s],0});d[innum[s]]=0; while(!q.empty()){ road t=q.top();q.pop(); if(d[t.i]!=t.dis) continue; for(ll i=head[t.i];i!=NULL;i=e[i].nxt){ if(t.dis+e[i].w<d[e[i].v]){ ll v=e[i].v; d[v]=t.dis+e[i].w; q.push((road){v,d[v]}); } } } } int main(){ scanf("%lld%lld%lld",&n,&m,&s); inbuild(1,1,n);oubuild(1,1,n); for(ll i=1;i<=n;i++){ adde(innum[i],ounum[i],0); adde(ounum[i],innum[i],0); } while(m--){ ll opt,v,l,r,w;scanf("%lld",&opt); switch(opt){ case 1:{ scanf("%lld%lld%lld",&l,&v,&w); adde(innum[l],ounum[v],w); break; } case 2:{ scanf("%lld%lld%lld%lld",&v,&l,&r,&w); oumodify(1,l,r,innum[v],w); break; } case 3:{ scanf("%lld%lld%lld%lld",&v,&l,&r,&w); inmodify(1,l,r,ounum[v],w); break; } } } sssp(); for(ll i=1;i<=n;i++) if(d[ounum[i]]!=INF) printf("%lld%c",d[ounum[i]]," \n"[i==n]); else printf("-1%c"," \n"[i==n]); return 0; }
原文地址:https://www.cnblogs.com/DWVictor/p/11203343.html