题目:

为了随时与rainbow快速交流,Freda制造了两部传呼机。Freda和rainbow所在的地方有N座房屋、M条双向光缆。每条光缆连接两座房屋,传呼机发出的信号只能沿着光缆传递,并且传呼机的信号从光缆的其中一端传递到另一端需要花费t单位时间。现在Freda要进行Q次试验,每次选取两座房屋,并想知道传呼机的信号在这两座房屋之间传递至少需要多长时间。Freda和rainbow简直弱爆了有木有T_T,请你帮帮他们吧……
N座房屋通过光缆一定是连通的,并且这M条光缆有以下三类连接情况:
A:光缆不形成环,也就是光缆仅有N-1条。
B:光缆只形成一个环,也就是光缆仅有N条。
C:每条光缆仅在一个环中

颂芬数据占10%,2<=N<=1000,N-1<=M<=1200。
A类数据占30%,M=N-1。
B类数据占50%,M=N。
C类数据占10%,M>N。
对于100%的数据,2<=N<=10000,N-1<=M<=12000,Q=10000,1<=x,y<=N,1<=t<32768。

分析:

(对于直接想要AC的人,可以直接忽略此部分)

可以看到:对于10%的数据,可以简简单单跑一个SPFA,(但一定要注意细节,严格按照模板来),下面给出10分的代码(通往AC的路是循序渐进的):

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
struct point
{int to,nxt,w;
}edge[52010];
int n,m,Q,a,b,ww,cnt=0;
int head[52010],vis[52010];
long long dis[22010];void init()
{memset(head,0,sizeof(head));cnt=0;
}void add(int u,int v,int wei)
{cnt++;edge[cnt].to=v;edge[cnt].nxt=head[u];edge[cnt].w=wei;head[u]=cnt;
}void spfa(int st)
{int fron=0,tail=1;int q[120000];memset(vis,0,sizeof(vis));memset(dis,0x7f,sizeof(dis));vis[st]=1;dis[st]=0;q[1]=st;do{fron++;int tt=q[fron];vis[tt]=0;for(int i=head[tt];i;i=edge[i].nxt){int v=edge[i].to;if(dis[v]>dis[tt]+edge[i].w) {dis[v]=dis[tt]+edge[i].w;if(!vis[v]){vis[v]=1;tail++;q[tail]=v; }                }}    }while(fron!=tail);
}int main()
{cin>>n>>m>>Q;init();for(int i=1;i<=m;i++){cin>>a>>b>>ww;add(a,b,ww);add(b,a,ww);}while(Q--){cin>>a>>b;spfa(a);cout<<dis[b]<<endl;}return 0;
}

View Code

剩下的能跑出树上倍增的,相信离成功也不远了。仔细看看题目中红色的字体,会发现实际上所有的环只可能有公共顶点,不可能有公共边,这样画出来就很像一个仙人掌(其实名称都不重要),那么我们该如何处理这样的一个个环呢?其实可以想到,我们以每一个公共顶点为树根,可以把多个环转化成一棵树,其中树枝长就是环中每个点到顶点的最短距离,但一定要分别记录每个点从两边到环顶的距离 l[i] 和 r[i],因为转换成一棵树后,从树上看来似乎是每两个点之间的路径必过顶点,但实际上在环中两个点完全可以不通过顶点而相互到达,因此两个点若在一个环中(这里实现的时候用一个数组分别记录每个点所在的环的编号和环顶),就有:

dis[x][y]= min( l[x]+r[y] , l[y]+r[x] , abs(r[x]-r[y]) );//一左一右到环顶,一右一左到环顶,和不通过环顶

然后至于树上倍增,我们用fa[x][i]表示从x这个节点往上2^i步能到的节点,用dis[x][i]表示从x这个节点到fa[x][i]这个祖先的距离;

就有初始化(递推):

fa[x][i]=fa[fa[x][i-1]][i-1];//从上一个位置再走一步
dis[x][i]=dis[fa[x][i-1]][i-1]+dis[x][i-1];//走半截再走半截

于是此题就可以AC了:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn=121000;
struct point1
{int to,nxt,w;
}edge[maxn<<1];
struct point2
{int to,nxt,w;
}edge2[maxn<<1];
int n,m,Q,a,b,ww,ss=0,ttt=0,n_cir=0;
int lop[maxn][4];//详见62~65行
int e[maxn][4],pre[maxn][3],bin[20],dfn[maxn],dep[maxn],head1[maxn],head2[maxn];
/*pre[i][0/1],0为i前一个是谁,1为i前一条边长*/
int dis[maxn][20],fa[maxn][20];void init()
{bin[0]=1;for(int i=1;i<=18;i++)bin[i]=bin[i-1]<<1;
}int cnt=0;
void add(int u,int v,int wei)
{edge[cnt].to=v;edge[cnt].nxt=head1[u];edge[cnt].w=wei;head1[u]=cnt++;
}int ct=0;
void ins(int u,int v,int wei)
{edge2[ct].to=v;edge2[ct].nxt=head2[u];edge2[ct].w=wei;head2[u]=ct++;}void dfs(int x,int y,int fa)
{ttt++;dfn[x]=ttt;for(int i=head1[x];~i;i=edge[i].nxt){int v=edge[i].to;if(v==fa && i==(y^1)) continue;if(dfn[v]!=0 && dfn[v]<dfn[x])//找到一个环
        {int len=edge[i].w;n_cir++;for(int j=ss;pre[j][0]!=v;j--)len+=pre[j][1];lop[x][0]=n_cir;//环的编号lop[x][1]=v;//环的顶点 lop[x][2]=edge[i].w;//从一边到顶点的距离 lop[x][3]=len-lop[x][2];//从另一边 ins(v,x,min(lop[x][2],lop[x][3]));//正反建图ins(x,v,min(lop[x][2],lop[x][3]));//按最短路径重新建图 for(int j=ss-1;pre[j][0]!=v;j--){int z=pre[j][0];lop[z][0]=n_cir;lop[z][1]=v;lop[z][2]=lop[pre[j+1][0]][2]+pre[j+1][1];lop[z][3]=len-lop[z][2];ins(v,z,min(lop[z][2],lop[z][3]));ins(z,v,min(lop[z][2],lop[z][3]));} } if(dfn[v])continue;pre[++ss][0]=v;pre[ss][1]=edge[i].w;dfs(v,i,x);}ss--;
}void dfss(int x)
{for(int i=1;i<=18;i++){if(dep[x]<bin[i]) break;fa[x][i]=fa[fa[x][i-1]][i-1];dis[x][i]=dis[fa[x][i-1]][i-1]+dis[x][i-1];}for(int i=head2[x];~i;i=edge2[i].nxt){if(edge2[i].to!=fa[x][0]){int v=edge2[i].to;fa[v][0]=x;dep[v]=dep[x]+1;//深度在待会lca要用 dis[v][0]=edge2[i].w;dfss(v);}}
}int lca(int x,int y)
{int sum=0;if(dep[x]<dep[y]) swap(x,y);int t=dep[x]-dep[y];for(int i=0;i<=18;i++)if(t&bin[i])//y在x下面,t的二进制在i那一位上有1 (保证要用2^i凑齐t)
        {sum+=dis[x][i];x=fa[x][i];}for(int i=18;i>=0;i--){if(fa[x][i]!=fa[y][i]){sum+=dis[x][i]+dis[y][i];x=fa[x][i];y=fa[y][i];}}if(x==y) return sum;if(lop[x][0]==lop[y][0] && lop[x][0]!=0)sum+=min(min(lop[x][2]+lop[y][3],lop[y][2]+lop[x][3]),abs(lop[x][2]-lop[y][2]));elsesum+=dis[x][0]+dis[y][0];return sum;
}int main()
{memset(head1,-1,sizeof(head1));memset(head2,-1,sizeof(head2));init();cin>>n>>m>>Q;for(int i=1;i<=m;i++){cin>>a>>b>>ww;add(a,b,ww);add(b,a,ww);e[i][1]=a;e[i][2]=b;e[i][3]=ww;}ss=1;pre[1][0]=1;pre[1][1]=0;dfs(1,-1,0);//找环,并计算出每个点到环顶的最短路 for(int i=1;i<=m;i++){int aa=e[i][1];int bb=e[i][2];int cc=e[i][3];if( (lop[aa][0]!=lop[bb][0] || !lop[aa][0] || !lop[bb][0]) && lop[aa][1]!=bb && lop[bb][1]!=aa){ins(aa,bb,cc);ins(bb,aa,cc);    } } dfss(1);//倍增处理 while(Q--){cin>>a>>b;cout<<lca(a,b)<<endl;}return 0;
}

转载于:https://www.cnblogs.com/linda-fcj/p/7220305.html

「Nescafé26」 Freda的传呼机 【最短路径+树上倍增】相关推荐

  1. 「Nescafé26」 Freda的传呼机 【树上倍增+图论】

    题目: 为了随时与rainbow快速交流,Freda制造了两部传呼机.Freda和rainbow所在的地方有N座房屋.M条双向光缆.每条光缆连接两座房屋,传呼机发出的信号只能沿着光缆传递,并且传呼机的 ...

  2. tyvj P2018 「Nescafé26」小猫爬山 解题报告

    P2018 「Nescafé26」小猫爬山 时间: 1000ms / 空间: 131072KiB / Java类名: Main 背景 Freda和rainbow饲养了N只小猫,这天,小猫们要去爬山.经 ...

  3. 「IOI2018」Highway 高速公路收费

    目录 「IOI2018」Highway 高速公路收费 题目描述: 实现细节: 输入格式: 输出格式: 样例: 数据范围与提示: 子任务: 题解: Code 「IOI2018」Highway 高速公路收 ...

  4. 「收藏」关于机器学习的知识点,全在这篇文章里了

    尊重原创版权: https://www.qingtianxiaoshuo.com/hot/44432.html 更多内容参考: https://www.qingtianxiaoshuo.com/ 「收 ...

  5. 图解:数据结构中的6种「树」,柠檬问你心中有数吗?

    数据结构这门课程是计算机相关专业的基础课,数据结构指的是数据在计算机中的存储.组织方式. 我们在学习数据结构时候,会遇到各种各样的基础数据结构,比如堆栈.队列.数组.链表.树...这些基本的数据结构类 ...

  6. 「走过」微软、优步,老工程师告诉你哪些数据结构和算法最重要

    数据结构和基础算法作为计算机科学的必学课程,近几年却关注度越来越少.但程序员真的不再需要这两门基础知识了吗?一位在 Uber 等科技公司工作过的开发者分享了他的一手经验,告诉你实际工作中会用到哪些数据 ...

  7. Loj #3055. 「HNOI2019」JOJO

    Loj #3055. 「HNOI2019」JOJO JOJO 的奇幻冒险是一部非常火的漫画.漫画中的男主角经常喜欢连续喊很多的「欧拉」或者「木大」. 为了防止字太多挡住漫画内容,现在打算在新的漫画中用 ...

  8. 特斯拉大半夜「见鬼」!空无一人的路上,它却看见「幽灵」秒刹车

    金磊 贾浩楠 发自 凹非寺 量子位 报道 | 公众号 QbitAI 讲个「鬼故事」: 夜深人静,一辆特斯拉Model X在空无一人的公路上行驶着. 瞬间!它看到了「人类看不见的东西」,于是便刹车在路上 ...

  9. LOJ#2230. 「BJOI2014」大融合

    LOJ#2230. 「BJOI2014」大融合 题目描述 小强要在$N$个孤立的星球上建立起一套通信系统.这套通信系统就是连接$N$个点的一个树.这个树的边是一条一条添加上去的. 在某个时刻,一条边的 ...

最新文章

  1. c语言生成随机坐标,C语言 文件的随机读写详解及示例代码
  2. PHP 表单 - 4(验证邮件和URL)
  3. OD的hit跟踪和run跟踪
  4. Leecode 69. x 的平方根
  5. TypeError: 'numpy.int64' object is not iterable ,'int' object is not iterable
  6. linux安装redis有什么用吗,Linux下 安装Redis
  7. http 网页突然报502 bad gateway,平台宕掉
  8. 共享文件 麒麟系统_麒麟操作系统安装手册.doc
  9. typora用Pandoc导出html,typora使用pandoc导出功能
  10. JavaScript 实现网页截屏五种方法
  11. 只有长大了,才认识父亲
  12. CASAIM全自动3d测量仪自动检测差速器差壳全尺寸测量装配检测
  13. 2022年迎接“金三银四”,为什么面试你总拿不到高薪?你所不知道的面试技巧
  14. 关于红黑树:了解是什么? 为什么设计? 会有什么效果? 什么时候用?
  15. Docker部署Jenkins服务
  16. 6.PMAC下位机-下位机编程
  17. cp: omitting directory xxx
  18. Go语言自学系列 | golang中的if语句
  19. 一剂拯救“国足”的终极药方:全面 AI 化
  20. 光伏组件机器视觉新突破!维视智造上线汇流带引线焊接检测新方案 “误检率”低至0.01%

热门文章

  1. 生鲜配送app开发方案
  2. 作为 Gopher, 你知道 Go 的注释即文档应该怎么写吗
  3. Round12—Huffman 树
  4. Vue-cli 2.0使用淘宝镜像搭建总结
  5. 优秀logo设计解析_优秀logo设计作品及寓意解析,国外精品logo设计图片分享
  6. wordpress友联_WordPress 友情链接页面终极版
  7. a标签在微信iOS版本的解析没有问题,但是在安卓版就解析不出来
  8. Windows安装VMware(Linux系统)
  9. CDMA码片序列问题
  10. Sun Java System Message Queue - Packet acknowledge failed after failover