[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 }

原文地址:https://www.cnblogs.com/dedicatus545/p/8457131.html

时间: 2024-10-02 07:05:11

[usaco jan 09] 安全路径 travel [最短路径树]的相关文章

[usaco jan 09] 气象牛 baric [dp]

题面: 传送门 思路: 题意有点绕,实际上就是给你一个计算规则,让你取最少的元素,通过这个计算方式,得到一个小于指定误差上限的结果 这个规则分为三个部分,这里分别用pre,sum,suf表示 因为给定的元素个数(天数)很少,可以使用O(n^3)算法,因此考虑使用经过了预处理的dp解决问题 具体地,设dp[i][j]表示前i个元素使用了j个且一定取用了第i个时可能达到的最小误差值 预处理:pre[i]表示通过第一种计算方式得到的,第一个元素取第i个时得到的误差 suf[i]同理,为第三种计算方式

[BZOJ 1576] 安全路径 最短路径树 路径压缩

题意 给定一张 n 个点 m 条边的图, 保证对于任意的点 i , 从点 1 到点 i 的最短路唯一. 对于任意的点 i , 询问: 将 1 到 i 的最短路中最后一条边删去之后, 从 1 到 i 的最短路 . n <= 100000, m <= 200000 . 分析 首先跑 Dijsktra , 构建出最短路径树. 接下来考虑每条非树边 E[p] = (u, v, d) 对答案的影响, 它能且仅能影响到点 u, v 之上, LCA(u, v) 之下的点的答案. (包括 u, v, 不包括

hdu 2433 Travel (最短路径树)

hdu 2433 Travel Description One day, Tom traveled to a country named BGM. BGM is a small country, but there are N (N <= 100) towns in it. Each town products one kind of food, the food will be transported to all the towns. In addition, the trucks will

[USACO09JAN]安全出行Safe Travel(最短路径树)

传送门 求对于每个点删掉1到他的最短路上的最后一条边(就是这条路径上与他自己相连的那条边)后1到他的最短路的长度. 即:最短路径树:图中的源点到所有结点的最短路径构成的树. 最短路径树在dijkstra过程中就可以求出来,因为这个过程就相当于走一棵树. 然后就是选入杂边,对于一条杂边<u,v>,它加入后会形成一个环,这个环上所有的点除了lca都可以被这条杂边更新,即这些点删去它上面那条边后是可以走杂边的,但lca删去上面那条边后图就不连通了. 那么 disnew[x]=dis[u]+dis[v

安全路径——最短路径树+dsu缩边

题目描述 思路 首先想到$dijkstra$跑完之后$build$一棵最短路径树.要找到每个节点i到根的满足要求的最短路,考虑把一些非树边加进去. 对于非树边$(u,v)$,因为节点i上方的边被占领,所以只能选择往下走,从非树边走到别的子树,设$u$属于$i$的子树,$v$不属于,那么$u,v$的$lca$经过$i$,且$i$经过$(u,v)$到根的最短路为$dist[u]+dist[v]-dist[i]+w(u,v)$,这样我们把每条非树边按照$dist[u]+dist[v]+w(u,v)$排

bzoj 4016 [FJOI2014]最短路径树问题(最短路径树+树分治)

4016: [FJOI2014]最短路径树问题 Time Limit: 5 Sec  Memory Limit: 512 MBSubmit: 426  Solved: 147[Submit][Status][Discuss] Description 给一个包含n个点,m条边的无向连通图.从顶点1出发,往其余所有点分别走一次并返回. 往某一个点走时,选择总长度最短的路径走.若有多条长度最短的路径,则选择经过的顶点序列字典序最小的那条路径(如路径A为1,32,11,路 径B为1,3,2,11,路径B

BZOJ4016: [FJOI2014]最短路径树问题

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=4016 最短路+点分治.. 首先要把最短路径树做出来吧..于是先跑一遍spfa,然后bfs一遍就可以建出树了.. 然后点分..对于以重心为根的那棵子树,一棵一棵子树拿出来,维护处dep和dis(到根的距离),然后更新答案. 设g[i][0]为深度为i的路径的最大距离,g[i][1]为当前这个最大距离下的路径个数. 这样维护还算是比较简单的吧... 感人肺腑的是没怎么调,过样例就A了..更感人肺

【bzoj4016】[FJOI2014]最短路径树问题 堆优化Dijkstra+DFS树+树的点分治

题目描述 给一个包含n个点,m条边的无向连通图.从顶点1出发,往其余所有点分别走一次并返回. 往某一个点走时,选择总长度最短的路径走.若有多条长度最短的路径,则选择经过的顶点序列字典序最小的那条路径(如路径A为1,32,11,路径B为1,3,2,11,路径B字典序较小.注意是序列的字典序的最小,而非路径中节点编号相连的字符串字典序最小).到达该点后按原路返回,然后往其他点走,直到所有点都走过. 可以知道,经过的边会构成一棵最短路径树.请问,在这棵最短路径树上,最长的包含K个点的简单路径长度为多长

【BZOJ4016】[FJOI2014]最短路径树问题 最短路径树+点分治

[BZOJ4016][FJOI2014]最短路径树问题 Description 给一个包含n个点,m条边的无向连通图.从顶点1出发,往其余所有点分别走一次并返回. 往某一个点走时,选择总长度最短的路径走.若有多条长度最短的路径,则选择经过的顶点序列字典序最小的那条路径(如路径A为1,32,11,路径B为1,3,2,11,路径B字典序较小.注意是序列的字典序的最小,而非路径中节点编号相连的字符串字典序最小).到达该点后按原路返回,然后往其他点走,直到所有点都走过. 可以知道,经过的边会构成一棵最短