题目链接
看到区间连边最短路,考虑优化连边。发现直接重链剖分线段树时空分别是$O(mlog^3n)$和$O(mlog^2n)$的,被卡掉了。考虑树上倍增,用类似ST表的方法优化连边,更优。
引用别人题解的一段话:优化连边通常就是考虑一种数据结构$S$,这个$S$一般具有分治结构,且$S$的每一个节点都是图上的一个节点。建立一个入$S$和出$S$,其中出$S$的边由分治结构的子区间指向父亲,入$S$的边由父亲指向儿子,边权都为$0$。每次从$[u_1,v_1]$向$[u_2,v_2]$连边时,先新建虚点$T$,在出$S$上提取出$[u_1,v_1]$向$T$连边,在入$S$上提取出$[u_2,v_2]$并从$T$向其连边,容易看出这和暴力连边是等效的。
读入并连原生树边的同时的维护并查集筛掉不合法的操作。暂时先存起合法的1操作。
因为这是个森林,我们挨个尝试跑dfs。
设$f(u,l)$表示点$u$的$2^l$级祖先,$fi(u,l)$和$fo(u,l)$表示 点$u$一直到它的$2^l-1$级祖先构成的长度为$2^l$链 分别在入结构和出结构上的代表节点 对应的图上的点 的编号。那么在入结构里,我们有边$fi(u,l) o fi(u,l-1)$和边$fi(u,l) o fi(f(u,l-1),l-1)$。在出结构里有两条反向的边。特别地,$fi(u,0)=fo(u,0)=u$。
大概是这么个结构(虚线表示连着相同左端点的其他“结构点”):
连好了“结构边”,接下来连“额外边”。
对于树上的路径,先从LCA处切成两条链,然后利用类似ST表的方法连边。设点$u$的$k$级祖先函数$anc(u,k)$,对于长度为$l$的链$(u,fa)$且有$2^tle l< 2^{t+1}$,选出$fi(u,t)$和$fi(anc(u,l-2^t),t)$即可。
接着跑堆优化Dijkstra求单源最短路。
图中点的个数:$n$个原生树点、$2nlog n$个结构点和$m$个额外点。
图中有向边的个数:$4nlog n$条结构边和$8m$条额外边。
代码(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; const int N=50000; const int M=1e6; const int L=15; const int V=N+2*N*L+M; const int E=4*N*L+8*M; const int inf=1e9; int n,m,S; struct Ufs{ int f[N+3],sz[N+3]; IL void init(){ for(int i=1;i<=n;i++){ f[i]=i; sz[i]=1; } } int find(int x){ return f[x]==x?x:f[x]=find(f[x]); } IL void merge(int x,int y){ x=find(x); y=find(y); if(sz[x]<sz[y]) swap(x,y); f[y]=x; sz[x]+=(int)(sz[x]==sz[y]); } IL bool qry(int x,int y){ return find(x)==find(y); } }ufs; struct Edge{ int to,nxt,cap; }e[E+3]; int top,img,h[V+3]; IL void gra_init(){ top=-1; img=n; memset(h,-1,sizeof h); } IL void link(int u,int v,int w){ top++; e[top].to=v; e[top].nxt=h[u]; e[top].cap=w; h[u]=top; } struct Opt{ int u1,v1,u2,v2,w; Opt(){} Opt(int u1,int v1,int u2,int v2,int w) :u1(u1),v1(v1),u2(u2),v2(v2),w(w){} }a[M+3]; int mm; int f[N+3][L+3],dep[N+3]; int fi[N+3][L+3],fo[N+3][L+3]; void dfs(int u,int fr){ for(int l=1;(1<<l)<=dep[u];l++){ f[u][l]=f[f[u][l-1]][l-1]; fi[u][l]=++img; fo[u][l]=++img; link(fi[u][l],fi[u][l-1],0); link(fi[u][l],fi[f[u][l-1]][l-1],0); link(fo[u][l-1],fo[u][l],0); link(fo[f[u][l-1]][l-1],fo[u][l],0); } for(int i=h[u];~i;i=e[i].nxt){ int v=e[i].to; if(v==fr||v>n) continue; f[v][0]=u; dep[v]=dep[u]+1; fi[v][0]=fo[v][0]=v; dfs(v,u); } } int lg2[N+3]; IL void lca_init(){ lg2[1]=0; for(int i=2;i<=n;i++) if(i==(1<<(lg2[i-1]+1))) lg2[i]=lg2[i-1]+1; else lg2[i]=lg2[i-1]; } IL int anc(int u,int k){ for(int l=lg2[dep[u]];l>=0;l--) if((1<<l)<=k){ u=f[u][l]; k-=1<<l; } return u; } IL int lca(int x,int y){ if(dep[x]>dep[y]) swap(x,y); y=anc(y,dep[y]-dep[x]); if(x==y) return x; for(int l=lg2[dep[x]];l>=0;l--) if(f[x][l]!=f[y][l]){ x=f[x][l]; y=f[y][l]; } return f[x][0]; } IL void link(int u,int fa,int x,int w,int t){ int l=dep[u]-dep[fa]+1,k=lg2[l]; int v=anc(u,l-(1<<k)); if(t==1){ link(fo[u][k],x,w); link(fo[v][k],x,w); } else { link(x,fi[u][k],w); link(x,fi[v][k],w); } } struct Dat{ int v,d; Dat(){} Dat(int v,int d):v(v),d(d){} }; IL bool operator<(Dat x,Dat y){ return x.v>y.v; } int dis[V+3]; priority_queue<Dat>hp; IL void dij(int S){ for(int i=1;i<=img;i++) dis[i]=inf; hp.push(Dat(dis[S]=0,S)); while(!hp.empty()){ Dat x=hp.top(); hp.pop(); if(dis[x.d]<x.v) continue; 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,&m,&S); ufs.init(); gra_init(); mm=0; for(int i=1;i<=m;i++){ int opt; scanf("%d",&opt); if(opt==1){ int u1,v1,u2,v2,w; scanf("%d%d%d%d%d",&u1,&v1,&u2,&v2,&w); if(ufs.qry(u1,v1)&&ufs.qry(u2,v2)) a[++mm]=Opt(u1,v1,u2,v2,w); } else { int u,v,w; scanf("%d%d%d",&u,&v,&w); if(ufs.qry(u,v)) continue; ufs.merge(u,v); link(u,v,w); link(v,u,w); } } memset(dep,0,sizeof dep); for(int i=1;i<=n;i++) if(!dep[i]){ f[i][0]=0; dep[i]=1; fi[i][0]=fo[i][0]=i; dfs(i,0); } lca_init(); for(int i=1;i<=mm;i++){ int ans; img++; ans=lca(a[i].u1,a[i].v1); link(a[i].u1,ans,img,0,1); link(a[i].v1,ans,img,0,1); ans=lca(a[i].u2,a[i].v2); link(a[i].u2,ans,img,a[i].w,2); link(a[i].v2,ans,img,a[i].w,2); } dij(S); for(int i=1;i<=n;i++) if(dis[i]==inf) printf("-1 "); else printf("%d ",dis[i]); return 0; }