一道数据结构与图论的综合的好题,有着两种解法,可以在这里提交:http://218.28.19.228/cogs/problem/problem.php?pid=279
【题解】
由于1到其他点的最短路唯一,所以以1为起点的最短路恰好构成一棵树(最短路树,树边i满足:d[v[i]]==d[u[i]]+w[i])
那么,一条不经过最短路径最后一条边的次短路,必然包含一条边(u,v)不在最短路树上(否则最短路树会形成环)那么这个K[i]能够更新哪些点x呢?
如图,能够更新:除LCA(ui,vi)外所有环上的点(update:vi到LCA(ui,vi)路径上的点)
K[i]不直接更新每个点,而是树链成段更新,线段树懒标记维护K[i]的最小值
好像叫路径剖分?
【代码】
注意,此题卡SPFA!
Dijkstra手写堆的给跪了。。。
#include<stdio.h> #include<stdlib.h> #define INF 1000000000 int u[400005]={0},v[400005]={0},w[400005]={0},first[100005]={0},next[400005]={0},d[100005]={0},heap[100005]={0},post[100005]={0}; int fa[100005][25]={0},deep[100005]={0},size[100005]={0},son[100005]={0},top[100005]={0},pos[100005]={0}; int minv[1000005]={0},tag[1000005]={0}; int n,e=0,tot=0; int min(int a,int b) { if(a<b) return a; return b; } void jh(int* a,int* b) { int t=*a; *a=*b; *b=t; } void tj(int x,int y,int z) { u[++e]=x; v[e]=y; w[e]=z; next[e]=first[x]; first[x]=e; } void Dijkstra(int n) { int i,j,k,cnt=n,x; for(i=2;i<=n;i++) d[i]=INF; for(i=1;i<=n;i++) heap[i]=pos[i]=i; for(i=1;i<n;i++) { x=heap[1]; cnt=n-i; jh(&pos[heap[1]],&pos[heap[cnt+1]]); jh(&heap[1],&heap[cnt+1]); j=1; while(j*2<=cnt) { j*=2; if(j+1<=cnt&&d[heap[j]]>d[heap[j+1]]) j++; if(d[heap[j/2]]>d[heap[j]]) { jh(&pos[heap[j/2]],&pos[heap[j]]); jh(&heap[j/2],&heap[j]); } else break; } for(k=first[x];k!=0;k=next[k]) if(d[v[k]]>d[u[k]]+w[k]) { d[v[k]]=d[u[k]]+w[k]; j=pos[v[k]]; while(j!=1) { if(d[heap[j/2]]>d[heap[j]]) { jh(&pos[heap[j/2]],&pos[heap[j]]); jh(&heap[j/2],&heap[j]); j/=2; } else break; } } } } void dfs1(int x,int pre,int dep) { int i; fa[x][0]=pre; for(i=1;i<=20;i++) fa[x][i]=fa[fa[x][i-1]][i-1]; deep[x]=dep; size[x]=1; for(i=first[x];i!=0;i=next[i]) if(d[v[i]]==d[u[i]]+w[i]&&v[i]!=pre) { dfs1(v[i],x,dep+1); size[x]+=size[v[i]]; if(son[x]==0||size[son[x]]<size[v[i]]) son[x]=v[i]; } } void dfs2(int x,int t) { int i; top[x]=t; pos[x]=++tot; if(son[x]!=0) dfs2(son[x],t); for(i=first[x];i!=0;i=next[i]) if(d[v[i]]==d[u[i]]+w[i]&&v[i]!=fa[x][0]&&v[i]!=son[x]) dfs2(v[i],v[i]); } int LCA(int x,int y) { int i; if(deep[x]<deep[y]) jh(&x,&y);//保证x比y深 while(deep[x]>deep[y])//调到x,y同深度 { for(i=0;deep[x]-(1<<i+1)>=deep[y];i++); x=fa[x][i]; } while(x!=y) { for(i=0;fa[x][i]!=fa[y][i];i++); if(i>0) i--; x=fa[x][i]; y=fa[y][i]; } return x; } void xg(int K,int x,int y,int o,int left,int right) { int mid=(left+right)/2; if(x<=left&&right<=y) tag[o]=min(tag[o],K); else { if(x<=mid) xg(K,x,y,o*2,left,mid); if(y>mid) xg(K,x,y,o*2+1,mid+1,right); } minv[o]=min(minv[o],K); } void update(int y,int f,int K) { int ty=top[y],tf=top[f]; while(ty!=tf) { xg(K,pos[ty],pos[y],1,1,n); y=fa[ty][0]; ty=top[y]; } if(y!=f) xg(K,pos[f]+1,pos[y],1,1,n); } int cx(int x,int o,int left,int right,int t) { int mid=(left+right)/2; if(left==right) return min(t,minv[o]); if(x<=mid) return cx(x,o*2,left,mid,min(t,tag[o])); if(x>mid) return cx(x,o*2+1,mid+1,right,min(t,tag[o])); } int main() { int i,j,x,y,z,ans; scanf("%d%d",&n,&i); for(;i>0;i--) { scanf("%d%d%d",&x,&y,&z); tj(x,y,z); tj(y,x,z); } Dijkstra(n); dfs1(1,1,0); dfs2(1,1); for(i=1;i<=1000000;i++) minv[i]=tag[i]=INF; for(i=1;i<=e;i++) if(d[v[i]]<d[u[i]]+w[i]) update(v[i],LCA(u[i],v[i]),d[u[i]]+d[v[i]]+w[i]); for(i=2;i<=n;i++) { ans=cx(pos[i],1,1,n,INF); if(ans==INF) ans=-1; else ans-=d[i]; printf("%d\n",ans); } return 0; }