当前位置: 首页 > news >正文

Rada and the Chamomile Valley(Tarjan缩点+多源BFS)

https://codeforces.com/contest/2132/problem/F

思路:如果某条边是1到n必然经过的边(即任意一条1到n的路径都包含这条边),那么删掉这条边后,1和n必然不在同一个连通块中。

对于一个无向图,如果删掉一条边后图中的连通分量数增加了,则称这条边为桥或者割边。严谨来说,就是:假设有连通图 G={V,E}, e 是其中一条边(即 e∈E),如果 G−e 是不连通的,则边 e 是图 G 的一条割边(桥)。

因此1到n必然经过的边就是桥了,我们通过Tarjan算法缩点,那么原图就会变成一棵无向树。树中连接着每个节点(连通分量)就是原图中的桥了。接着还需要找到从1到n一定会经过的桥,这时只需要以1所在的连通分量为起点,对缩点后的树进行dfs,找到到达n所在连通分量的所有边(桥),将这些边构成的集合记为E,E中每条边的两个端点构成的集合记为V。

对于每次询问的节点u,如果每次都跑一遍bfs显然超时,考虑到每个查询都关注节点到V中节点的最短距离,我们可以转换思路:以V中节点作为源点进行多源bfs,这样得到的结果等价于原图中任意节点到V中节点的最短距离。在bfs过程中再额外维护距离节点最近的边的编号(E中的边),这样就可以通过一次bfs求得任意一个点的答案,查询时间O(1)。

大致步骤如下:

1.建原图,将编号看作边权

2.对原图跑Tarjan算法,确定每个节点所在连通分量的编号,每个连通分量的编号对应缩点后无向树的节点编号

3.接着建立无向树,遍历原图所有边,如果该边的两个端点不在同一个连通分量,则说明该边是桥,同时对这两个连通分量进行连边

4.然后以1所在的连通分量对应的节点为根,对无向树进行dfs,并在这个过程中维护一个数组用来记录访问到当前节点所经过的边的编号(对应边权)。当访问到n时,此时数组就记录了从1到n必然经过的边。

5.枚举所有被记录的边,将两端的端点压入到bfs队列中,为避免重复操作,要对已压入的点进行标记。在维护最短距离的同时,再维护到达V中节点编号最小的边。

Code:

//#define int long long
int h1[N],h2[N],w[N*4],ne[N*4],e[N*4],idx;
int cnt,tot,top,dfn[N],stk[N],low[N],scc[N];
int vis[N],n,m;void add(int *h,int a,int b,int c)
{e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}void Tarjan(int u,int fa)
{low[u]=dfn[u]=++tot;stk[++top]=u;for(int i=h1[u];~i;i=ne[i]){int j=e[i];if(!dfn[j]){Tarjan(j,i);low[u]=min(low[u],low[j]);}else if(i!=(fa^1)) low[u]=min(low[u],dfn[j]);}if(low[u]==dfn[u]){cnt++;int y;do{y=stk[top--];scc[y]=cnt;}while(y!=u);}
}bool dfs(int u,int fa)
{if(u==scc[n]) return true;for(int i=h2[u];~i;i=ne[i]){int j=e[i];if(j==fa) continue;vis[w[i]]=true;if(dfs(j,u)) return true;vis[w[i]]=false;}return false;
}
void solve()
{cin>>n>>m;memset(h1, -1,  n + 1 << 2);//int占4个字节,memset的第三位代表字节大小//如果用longlong,就要乘8memset(dfn, 0,n + 1 << 2);// memset(low,0,sizeof low);// memset(vis,0,sizeof vis);idx=0;for(int i=1;i<=m;i++){int a,b;cin>>a>>b;add(h1,a,b,i);add(h1,b,a,i);}tot=cnt=0;Tarjan(1,-1);memset(h2,-1, cnt + 1 << 2);for(int i=1;i<=n;i++){for(int j=h1[i];~j;j=ne[j]){int u=scc[i],v=scc[e[j]],c=w[j];if(u!=v) add(h2,u,v,c);}}dfs(scc[1],0);queue<int> q;vector<bool> st(n+1,false);vector<int> bh(n+1,1e9),dist(n+1,1e9);for(int i=1;i<=n;i++){for(int j=h1[i];~j;j=ne[j]){int u=i,v=e[j],c=w[j];if(vis[c]){bh[u]=min(bh[u],c);bh[v]=min(bh[v],c);if(!st[u]){q.push(u);dist[u]=0;st[u]=true;}if(!st[v]){q.push(v);dist[v]=0;st[v]=true;}vis[c]=false;}}}while(q.size()){auto t=q.front();q.pop();for(int i=h1[t];~i;i=ne[i]){int j=e[i];if(dist[j]>dist[t]+1){dist[j]=dist[t]+1;bh[j]=bh[t];q.push(j);}else if(dist[j]==dist[t]+1)bh[j]=min(bh[j],bh[t]);}}cin>>m;while(m--){int k;cin>>k;cout<<(bh[k]==1e9?-1:bh[k])<<' ';}cout<<endl;
}
signed main()
{ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);int t;cin>>t;//t=1;while(t--) solve();
}

参考博客:F. Rada and the Chamomile Valley - onlyblues - 博客园

http://www.dtcms.com/a/395031.html

相关文章:

  • Jfrog开源替代,推荐一款国产开源免费的制品管理工具 - Hadess
  • 《C++进阶之C++11》【列表初始化 + 右值引用】
  • 【Redis】:从应用了解Redis
  • 从零到一打造前端内存监控 SDK,并发布到 npm ——基于 TypeScript + Vite + ECharts的解决方案
  • 【星海随笔】RabbitMQ容器篇
  • OpenCV:人脸识别实战,3 种算法(LBPH/EigenFaces/FisherFaces)代码详解
  • OneSignal PHP SDK v2 版本实现指南
  • Dockerfile构建镜像以及网络
  • 鸿蒙音乐播放器基础实现
  • Vue3》》Vite》》文件路由 vite-plugin-pages、布局系统 vite-plugin-vue-layouts
  • 深入解析 MySQL MVCC:高并发背后的数据时光机
  • 汽车冷却系统的冷却水管介绍
  • 基础架构设计
  • 从分散到统一:基于Vue3的地图模块重构之路
  • JVM实际内存占用
  • Spark SQL 桶抽样(Bucket Sampling)
  • 常见的【垃圾收集算法】
  • 如何解决 pip install 安装报错 ModuleNotFoundError: No module named ‘django’ 问题
  • jvm之【垃圾回收器】
  • Tomcat基础知识
  • Will、NGC游戏模拟器 Dolphin海豚模拟器2509最新版 电脑+安卓版 附游戏
  • ELK企业级日志分析系统详解:从入门到部署实践
  • 2025年Spring Security OAuth2实现github授权码模式登录
  • Kafka面试精讲 Day 22:Kafka Streams流处理
  • ELK大总结20250922
  • 基于Hadoop生态的汽车全生命周期数据分析与可视化平台-基于Python+Vue的二手车智能估价与市场分析系统
  • 基于TV模型利用Bregman分裂算法迭代对图像进行滤波和复原处理
  • 利用 Perfmon.exe 与 UMDH 组合分析 Windows 程序内存消耗
  • hello算法笔记 02
  • 二级域名解析与配置