[usaco jan 09] 安全路径 travel [最短路径树]

Posted Orion545

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[usaco jan 09] 安全路径 travel [最短路径树]相关的知识,希望对你有一定的参考价值。

题面:

传送门

思路:

既然最后一条边不能走,那么就一定是换了一条路,一条不经过这最后一条边的路

如果想要这条路最短,那么其在路上一定尽可能多地走了最短路径

因此,我们对这张图跑一遍从1开始的单源最短路,并建立出最短路径树

那么新的路径(1->u)一定是这样构成的:(1->v)+edge(v,w)+(w->u),其中w是u在最短路径树上的后代

那么,我们对于每一条非树边(u,v),其树上路径上所有点(除了lca)的答案,都可以被dis[u]+dis[v]+w(u,v)-dis[路径上的点]来更新

然而,直接每一次这样更新,时间效率肯定不行

对上述的结论进一步分析,可以发现,实际上dis[u]+dis[v]+w(u,v)对于每条非树边(u,v)固定

因此可以把dis[u]+dis[v]+w(u,v)排序,对于每一条非树边依次操作,并且使用并查集来跳过已经更新过得边

总时间复杂度:dij O(nlogn),排序 O(mlogm) 更新答案 O(n)

Code:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<queue>
 6 #include<vector>
 7 using namespace std;
 8 struct edge{
 9     int to,w,next;
10 }e[400100];
11 struct new_edge{
12     int from,to,w;
13 }x[400100];
14 struct node{
15     int num,fa;
16     vector<int>son;
17 }a[100100];
18 int n,m,first[100100],pre[100100],dis[100100],f[100100],ans[100100];
19 bool vis[100100];
20 void spfa(){
21     int i,u,v,w;
22     queue<int>q;memset(dis,127,sizeof(dis));
23     q.push(1);pre[1]=1;dis[1]=0;
24     while(!q.empty()){
25         u=q.front();q.pop();vis[u]=0;
26         for(i=first[u];~i;i=e[i].next){
27             v=e[i].to;w=e[i].w;
28             if(dis[v]>dis[u]+w){
29                 dis[v]=dis[u]+w;pre[v]=u;
30                 if(!vis[v]){
31                     vis[v]=1;q.push(v);
32                 }
33             }
34         }
35     }
36 }
37 bool cmp(new_edge l,new_edge r){
38     return l.w<r.w;
39 }
40 int find(int x){
41     return ((f[x]==x)?x:f[x]=find(f[x]));
42 }
43 int aa[200100],bb[200100],cc[200100];
44 int main(){
45     freopen("travel.in","r",stdin);
46     freopen("travel.out","w",stdout);
47     memset(first,-1,sizeof(first));
48     memset(ans,-1,sizeof(ans));
49     int i,j,t1,t2,t3,l,r;
50     scanf("%d%d",&n,&m);
51     for(i=1;i<=m;i++){
52         scanf("%d%d%d",&t1,&t2,&t3);
53         e[i*2-1].to=t2;e[i*2-1].next=first[t1];e[i*2-1].w=t3;first[t1]=i*2-1;
54         e[i*2].to=t1;e[i*2].next=first[t2];e[i*2].w=t3;first[t2]=i*2;
55         aa[i]=t1;bb[i]=t2;cc[i]=t3;
56     }
57     spfa();
58 //    for(i=1;i<=n;i++) cout<<pre[i]<<ends<<dis[i]<<endl;
59 //    for(i=1;i<=m;i++) cout<<aa[i]<<ends<<bb[i]<<ends<<cc[i]<<endl;
60 //    cout<<"end of spfa"<<endl;
61     for(i=1;i<=n;i++){
62         a[i].num=i;f[i]=i;
63         a[i].fa=pre[i];
64         if(i!=1) a[pre[i]].son.push_back(i);
65     }
66     int cnt=0;
67     for(i=1;i<=m;i++){
68         if(pre[aa[i]]==bb[i]||pre[bb[i]]==aa[i]) continue;
69 //        cout<<aa[i]<<ends<<bb[i]<<ends<<cc[i]<<endl;
70         x[++cnt].from=aa[i];x[cnt].to=bb[i];x[cnt].w=dis[aa[i]]+dis[bb[i]]+cc[i];
71     }
72 //    cout<<"end2"<<endl;
73     sort(x+1,x+cnt+1,cmp);
74     for(i=1;i<=cnt;i++){
75         l=find(x[i].from);r=find(x[i].to);
76         while(l!=r){
77             if(dis[l]<dis[r]) swap(l,r);
78             ans[l]=x[i].w-dis[l];
79             f[l]=pre[l];l=find(l);
80         }
81     }
82     for(i=2;i<=n;i++) printf("%d\n",ans[i]);
83 }

 

以上是关于[usaco jan 09] 安全路径 travel [最短路径树]的主要内容,如果未能解决你的问题,请参考以下文章

[USACO09JAN]安全出行Safe Travel

[Usaco2009 Jan]安全路经Travel

BZOJ1576 [Usaco2009 Jan]安全路经Travel

P2934 [USACO09JAN]安全出行

[USACO09JAN]安全出行Safe Travel

1718: [Usaco2006 Jan] Redundant Paths 分离的路径