题目链接
单点向区间连边,暴力转化为向区间内的每个点连边,显然会超时。
考虑建起两颗线段树,连向区间的边都连向“入树”,从区间连出的边都从“出树”连出,同时“入树”内按从根到叶子再到原单点的方向连边,“出树”内按从原单点到叶子再到根的方向连边,虚边(结构边)权值为零。
用样例二举例,画图如下:
我的做法是设置了节点结构体,图里区间上的标号就是数组下标。详见代码。
用了链表方便算空间,一颗线段树$2n$个点,总共有$5n$个点。结构边$4n$条,每次加$O(log n)$条边,总共算$4n+qlog n$条边。
建好图之后跑堆优化Dijkstra就好了。
代码(100分):
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<queue> #include<map> #include<set> #define IL inline #define RG register #define _1 first #define _2 second using namespace std; typedef long long LL; const int N=1e5; const int L=17; const LL inf=1e14; int n,q,s; struct Node{ int l,r,ls,rs; }t[N*5+3]; int restop,tirt,tort; struct Edge{ int to,nxt; LL cap; }e[N*(6+L)+3]; int top,h[N*5+3]; IL void add(int u,int v,LL w){ top++; e[top].to=v; e[top].nxt=h[u]; e[top].cap=w; h[u]=top; } void build(int i,int l,int r,int opt){ //opt1: tr_in; opt2: tr_out t[i].l=l; t[i].r=r; if(l==r){ t[i].ls=t[i].rs=0; opt==1?add(i,l,0):add(l,i,0); return ; } int mid=(l+r)>>1; t[i].ls=++restop; build(t[i].ls,l,mid,opt); t[i].rs=++restop; build(t[i].rs,mid+1,r,opt); if(opt==1){ add(i,t[i].ls,0); add(i,t[i].rs,0);} else if(opt==2){ add(t[i].ls,i,0); add(t[i].rs,i,0);} } void mdf(int i,int ql,int qr,int u,LL w,int opt){ if(t[i].r<ql||qr<t[i].l) return ; if(ql<=t[i].l&&t[i].r<=qr){ opt==1?add(u,i,w):add(i,u,w); return ; } int mid=(t[i].l+t[i].r)>>1; if(ql<=mid) mdf(t[i].ls,ql,qr,u,w,opt); if(qr>mid) mdf(t[i].rs,ql,qr,u,w,opt); } LL dis[N*5+3]; struct Dat{ LL v; int d; Dat(){} Dat(LL p1,int p2):v(p1),d(p2){} }; IL bool operator<(Dat x,Dat y){ return x.v>y.v; } priority_queue<Dat>hp; IL void dij(int S){ for(int i=1;i<=restop;i++) dis[i]=inf; hp.push(Dat(dis[S]=0,S)); while(!hp.empty()){ Dat x=hp.top(); hp.pop(); while(dis[x.d]<x.v&&!hp.empty()){ x=hp.top(); hp.pop(); } int u=x.d; for(int i=h[u];~i;i=e[i].nxt){ int v=e[i].to; if(dis[u]+e[i].cap<dis[v]) hp.push(Dat(dis[v]=dis[u]+e[i].cap,v)); } } } int main(){ scanf("%d%d%d",&n,&q,&s); memset(h,-1,sizeof h); top=-1; restop=n; build(tirt=++restop,1,n,1); build(tort=++restop,1,n,2); while(q--){ int opt,u,v,l,r; LL w; scanf("%d%d",&opt,&u); if(opt==1){ scanf("%d%lld",&v,&w); add(u,v,w); } else { scanf("%d%d%lld",&l,&r,&w); mdf(opt==2?tirt:tort,l,r,u,w,opt-1); } } dij(s); for(int i=1;i<=n;i++) printf("%lld ",dis[i]!=inf?dis[i]:-1); return 0; }