时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述

还记得上次小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] 第五十二周: 连通性·一的更多相关文章

  1. hihocoder 第五十二周 高斯消元·二【高斯消元解异或方程 难点【模板】】

    题目地址:http://hihocoder.com/contest/hiho57/problem/1 输入 第1..5行:1个长度为6的字符串,表示该行的格子状态,1表示该格子是亮着的,0表示该格子是 ...

  2. 第十二周作业_PSP总结报告

    回顾1 (1)回想一下你曾经对计算机专业的畅想 当初你是如何做出选择计算机专业的决定的?经过一个学期,你的看法改变了么,为什么? 你认为过去接触到的课程是否符合你对计算机专业的期待,为什么?经过一个学 ...

  3. 2017-2018-1 《Linux内核原理与设计》第十二周作业

    <linux内核原理与设计>第十二周作业 Sql注入基础原理介绍 分组: 和20179215袁琳完成实验 一.实验说明   SQL注入攻击通过构建特殊的输入作为参数传入Web应用程序,而这 ...

  4. 201871010123-吴丽丽《面向对象程序设计(Java)》第十二周学习总结

    201871010123-吴丽丽<面向对象程序设计(Java)>第十二周学习总结 项目 内容 这个作业属于哪个课程  https://www.cnblogs.com/nwnu-daizh/ ...

  5. 201871010106-丁宣元 《面向对象程序设计(java)》第十二周学习总结

    201871010106-丁宣元 <面向对象程序设计(java)>第十二周学习总结 正文开头: 项目 内容 这个作业属于哪个课程 https://home.cnblogs.com/u/nw ...

  6. 201871010136-赵艳强《面向对象程序设计(java)》第十二周学习总结

    201871010136-赵艳强<面向对象程序设计(java)>第十二周学习总结   项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh ...

  7. 201871020225-牟星源《面向对象程序设计(java)》第十二周学习总结

    201871020225-牟星源<面向对象程序设计(java)>第十二周学习总结 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ ...

  8. 201521123061 《Java程序设计》第十二周学习总结

    201521123061 <Java程序设计>第十二周学习总结 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. 2. 书面作业 将Student对 ...

  9. 201521123072《java程序设计》第十二周学习总结

    201521123072<java程序设计>第十二周学习总结 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. 2. 书面作业 将Student对象 ...

随机推荐

  1. 移动端自适应布局 rem方案

    1.viewport.js (function(window, document) { // 给hotcss开辟个命名空间,别问我为什么,我要给你准备你会用到的方法,免得用到的时候还要自己写. con ...

  2. SpringBoot配置RestTemplate的代理和超时时间

    application.properties: #代理设置 proxy.enabled=false proxy.host=192.168.18.233 proxy.port=8888 #REST超时配 ...

  3. msiexec command line arguments

    Documented command line arguments Type MSIEXEC /? and you'll get the following on-screen help: Windo ...

  4. JS正则替换掉小括号及内容

    正則表達式:\ ( [ ^ \ ) ] * \ ) JS代码: var str="hello(world)"; var nstr = str.replace(/\([^\)]*\) ...

  5. 总结一下关于mysql 5.6 新特性

    一直断断续续的看一些mysql特性,今天总结一下,以下是列表,网址 http://mariadb.org/ (也是类似的特性), http://mysql.com/ 最近在看关于mysql新特性的一些 ...

  6. ACM经验分享[转]

    明确规则 规则:以最少的时间过题 (这意味着0ms与1000ms是一样的) 了解规则,善用规则 虽然这个题我不会但是AC是没有问题的 --ACRush 大力出奇迹 学会对拍数据,准备好对拍脚本:测试很 ...

  7. 老男孩linux实战培训初级班第二次课前考试题

    ################################################################ 本文内容摘录于老男孩linux实战运维培训中心课前考试题(答案部分) ...

  8. Linux(Ubuntu12.04)上玩儿STC单片机(转)

    操作系统:Ubuntu16.04 TLS 单片机:STC的STC89C52RC系列及 Atmel的AT89S52... 所需工具: 1.编辑器- Vim(不钟情于IDE,个人喜好,高手勿喷) 2.编译 ...

  9. 树莓派进阶之路 (008) - 树莓派安装ftp服务器(转)

    vsftpd是开源的轻量级的常用ftp服务器.   1,安装vsftpd服务器 (约400KB) sudo apt-get install vsftpd     2,启动ftp服务 sudo serv ...

  10. UNIX 家族及Linux

    Unix成长为一个非私有的操作系统,是因为1956年的AT&T公司受命于联邦去经营电报电话服务.当然也可以开发软件,甚至那个软件可以有”合理”收费的许可证,但是这个公司却被禁止从事任何和计算机 ...