[hihoCoder] 第五十二周: 连通性·一
描述
还记得上次小Hi和小Ho学校被黑客攻击的事情么,那一次攻击最后造成了学校网络数据的丢失。为了避免再次出现这样的情况,学校决定对校园网络进行重新设计。
学校现在一共拥有N台服务器(编号1..N)以及M条连接,保证了任意两台服务器之间都能够通过连接直接或者间接的数据通讯。
当发生黑客攻击时,学校会立刻切断网络中的一条连接或是立刻关闭一台服务器,使得整个网络被隔离成两个独立的部分。
举个例子,对于以下的网络:

每两个点之间至少有一条路径连通,当切断边(3,4)的时候,可以发现,整个网络被隔离为{1,2,3},{4,5,6}两个部分:

若关闭服务器3,则整个网络被隔离为{1,2},{4,5,6}两个部分:

小Hi和小Ho想要知道,在学校的网络中有哪些连接和哪些点被关闭后,能够使得整个网络被隔离为两个部分。
在上面的例子中,满足条件的有边(3,4),点3和点4。
输入
第1行:2个正整数,N,M。表示点的数量N,边的数量M。1≤N≤20,000, 1≤M≤100,000
第2..M+1行:2个正整数,u,v。表示存在一条边(u,v),连接了u,v两台服务器。1≤u<v≤N
保证输入所有点之间至少有一条连通路径。
输出
第1行:若干整数,用空格隔开,表示满足要求的服务器编号。从小到大排列。若没有满足要求的点,该行输出Null
第2..k行:每行2个整数,(u,v)表示满足要求的边,u<v。所有边根据u的大小排序,u小的排在前,当u相同时,v小的排在前面。若没有满足要求的边,则不输出
- 样例输入
- 
6 7 
 1 2
 1 3
 2 3
 3 4
 4 5
 4 6
 5 6
- 样例输出
- 
3 4 
 3 4
小Ho:这次的问题好简单!我只要依次删除每一个节点,每一条边,然后用DFS判断一下连通性就好了。
小Hi:没那么简单啦,好好看清楚N和M的范围啦。
小Ho:@_@,N和M怎么这么大,那应该怎么办?
小Hi:其实也很容易啊。这次我们要用到的叫做Tarjan算法。首先我给你普及一点点基本的知识:
割边:在连通图中,删除了连通图的某条边后,图不再连通。这样的边被称为割边,也叫做桥。
割点:在连通图中,删除了连通图的某个点以及与这个点相连的边后,图不再连通。这样的点被称为割点。
DFS搜索树:用DFS对图进行遍历时,按照遍历次序的不同,我们可以得到一棵DFS搜索树。在上面例子中,得到的搜索树为:

树边:在搜索树中的蓝色线所示,可理解为在DFS过程中访问未访问节点时所经过的边,也称为父子边
回边:在搜索树中的橙色线所示,可理解为在DFS过程中遇到已访问节点时所经过的边,也称为返祖边、后向边
观察DFS搜索树,我们可以发现有两类节点可以成为割点:
- 对根节点u,若其有两棵或两棵以上的子树,则该根结点u为割点;
- 对非叶子节点u(非根节点),若其子树的节点均没有指向u的祖先节点的回边,说明删除u之后,根结点与u的子树的节点不再连通;则节点u为割点。
对于根结点,显然很好处理;但是对于非叶子节点,怎么去判断有没有回边是一个值得深思的问题。
我们用dfn[u]记录节点u在DFS过程中被遍历到的次序号,low[u]记录节点u或u的子树通过非父子边追溯到最早的祖先节点(即DFS次序号最小),那么low[u]的计算过程如下:

对于给的例子,其求出的dfn和low数组为:
id 1 2 3 4 5 6
dfn 1 2 3 4 5 6
low 1 1 1 4 4 4
可以发现,对于情况2,当(u,v)为树边且low[v]≥dfn[u]时,节点u才为割点。
而当(u,v)为树边且low[v]>dfn[u]时,表示v节点只能通过该边(u,v)与u连通,那么(u,v)即为割边。
Tarjan算法的代码如下:
void dfs(int u) {
	//记录dfs遍历次序
	static int counter = 0;	
	//记录节点u的子树数
	int children = 0;
	ArcNode *p = graph[u].firstArc;
	visit[u] = 1;
	//初始化dfn与low
	dfn[u] = low[u] = ++counter;
	for(; p != NULL; p = p->next) {
		int v = p->adjvex;
		//节点v未被访问,则(u,v)为树边
		if(!visit[v]) {
			children++;
			parent[v] = u;
			dfs(v);
			low[u] = min(low[u], low[v]);
			//case (1)
			if(parent[u] == NIL && children > 1) {
				printf("articulation point: %d\n", u);
			}
			//case (2)
			if(parent[u] != NIL && low[v] >= dfn[u]) {
				printf("articulation point: %d\n", u);
			}
			//bridge
			if(low[v] > dfn[u]) {
				printf("bridge: %d %d\n", u, v);
			}
		}
		//节点v已访问,则(u,v)为回边
		else if(v != parent[u]) {
			low[u] = min(low[u], dfn[v]);
		}
	}
}
		
小Ho:我大概明白了,如果这样来做,时间复杂度应该也很低吧。
小Hi:没错,它的时间复杂度是O(n+m)的,非常快。
小Ho:好!看我来实现它!
注意可能有重边,另外要注意割点要去重!
#include <bits/stdc++.h>
using namespace std; int N, M;
int u, v;
vector<vector<int>> graph;
vector<int> dfn, low, parent;
vector<bool> visit;
set<int> art;
set<pair<int, int>> brg;
set<pair<int, int>> st1, st2; void dfs(int u) {
static int counter = ;
int children = ;
visit[u] = true;
dfn[u] = low[u] = ++counter;
for (auto v : graph[u]) {
if (!visit[v]) {
++children;
parent[v] = u;
dfs(v);
low[u] = min(low[u], low[v]);
if (parent[u] == && children > ) {
art.insert(u);
}
if (parent[u] != && low[v] >= dfn[u]) {
art.insert(u);
}
if (low[v] > dfn[u]) {
brg.insert({min(u, v), max(u, v)});
}
} else if (v != parent[u]) {
low[u] = min(low[u], dfn[v]);
}
}
} void solve() {
dfs();
if (art.empty()) {
cout << "Null" << endl;
} else {
for (auto a : art) cout << a << " ";
cout << endl;
}
if (!brg.empty()) {
for (auto b : brg) if (st2.find(b) == st2.end()) {
cout << b.first << " " << b.second << endl;
}
}
} int main() {
while (cin >> N >> M) {
graph.assign(N + , vector<int>());
dfn.assign(N + , );
low.assign(N + , );
parent.assign(N + , );
visit.assign(N + , false);
st1.clear(), st2.clear();
art.clear(), brg.clear();
for (int i = ; i < M; ++i) {
cin >> u >> v;
graph[u].push_back(v);
graph[v].push_back(u);
if (st1.find({min(u, v), max(u, v)}) != st1.end()) {
st2.insert({min(u, v), max(u, v)});
}
st1.insert({min(u, v), max(u, v)});
}
solve();
}
return ;
}
[hihoCoder] 第五十二周: 连通性·一的更多相关文章
- hihocoder 第五十二周 高斯消元·二【高斯消元解异或方程 难点【模板】】
		题目地址:http://hihocoder.com/contest/hiho57/problem/1 输入 第1..5行:1个长度为6的字符串,表示该行的格子状态,1表示该格子是亮着的,0表示该格子是 ... 
- 第十二周作业_PSP总结报告
		回顾1 (1)回想一下你曾经对计算机专业的畅想 当初你是如何做出选择计算机专业的决定的?经过一个学期,你的看法改变了么,为什么? 你认为过去接触到的课程是否符合你对计算机专业的期待,为什么?经过一个学 ... 
- 2017-2018-1 《Linux内核原理与设计》第十二周作业
		<linux内核原理与设计>第十二周作业 Sql注入基础原理介绍 分组: 和20179215袁琳完成实验 一.实验说明 SQL注入攻击通过构建特殊的输入作为参数传入Web应用程序,而这 ... 
- 201871010123-吴丽丽《面向对象程序设计(Java)》第十二周学习总结
		201871010123-吴丽丽<面向对象程序设计(Java)>第十二周学习总结 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ ... 
- 201871010106-丁宣元 《面向对象程序设计(java)》第十二周学习总结
		201871010106-丁宣元 <面向对象程序设计(java)>第十二周学习总结 正文开头: 项目 内容 这个作业属于哪个课程 https://home.cnblogs.com/u/nw ... 
- 201871010136-赵艳强《面向对象程序设计(java)》第十二周学习总结
		201871010136-赵艳强<面向对象程序设计(java)>第十二周学习总结 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh ... 
- 201871020225-牟星源《面向对象程序设计(java)》第十二周学习总结
		201871020225-牟星源<面向对象程序设计(java)>第十二周学习总结 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ ... 
- 201521123061 《Java程序设计》第十二周学习总结
		201521123061 <Java程序设计>第十二周学习总结 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. 2. 书面作业 将Student对 ... 
- 201521123072《java程序设计》第十二周学习总结
		201521123072<java程序设计>第十二周学习总结 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. 2. 书面作业 将Student对象 ... 
随机推荐
- Android设备广告投放解决方案——大量网络图片、多个网络视频的轮播、缓存与更新
			转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/7742996.html 一:业务场景 基于Android系统的设备上投放广告,诸如:地铁广告屏.自助服务机器上的 ... 
- 关于XCode更换项目名称
			1.打开项目直接修改项目名称 2.直接修改分组名 3.然后.command+B会报错 4.找到项目源文件 YourProject.xcodeproj - > 右键显示包内容->找到pro ... 
- eclipse svn新增文件不显示在文件列表,只有修改文件可以提交!
			1.情景展示 eclipse修改的文件可以正常提交,但是新增的文件没有显示在提交列表中,导致无法提交! 2.解决方案 选中要提交的文件-->右键-->Team-->提交 勾选上这 ... 
- Kinect2.0骨骼跟踪与数据平滑
			Kinect v1和Kinect v2传感器的配置比较: Kinect v1 Kinect v2 颜色(Color) 分辨率(Resolution) 640×480 1920× ... 
- 用Beautiful Soup解析html源码
			#xiaodeng #python3 #用Beautiful Soup解析html源码 html_doc = """ <html> <head> ... 
- GitLab概念——Group、Project、Member
			概念说明: Group是一个父子结构的目录 Group每一级都可以设置关联的Member,同时每一级下都可以创建项目 Group关联的Member和Member对应的权限,会继承到Group下的所有P ... 
- 基于SpringSecurity和JWT的用户访问认证和授权
			发布时间:2018-12-03 技术:springsecurity+jwt+java+jpa+mysql+mysql workBench 概述 基于SpringSecurity和JWT的用户访 ... 
- Swift3 substring几种常用用法
			举例: " let length = str.characters.count //截取前四位 )) //截取后2位(两种方法) )) )) //截取中间4位,从第2位开始(二种方法) le ... 
- maven 下载 源码和javadoc命令(转)
			转自:http://blog.csdn.net/topwqp/article/details/8902863 maven 下载 源码和javadoc命令 目录(?)[+] Maven命令下载源码和ja ... 
- 业务、架构、技术,我们应该关注什么 Java和.Net的优势劣势简单看法 市场经济决定,商业之道即是软件之道,市场的需求决定着软件技术的发展 利益决定着选择应用新技术
			业务.架构.技术,我们应该关注什么 一个企业存在的必然和前提就是获取企业生成的利润,怎么样合法合理取得利润呢,企业怎么样生存下去呢,很简单,为客户提供等值的产品与服务,客户支付你相应的报酬. 我们是从 ... 
