时间限制: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. linux 命令行cd dvd iso操作

    1 . 制作ISO: $ mkisofs -V LabelName -J -jcharset=utf8 -r -o /home/TargetImage.iso /home/my-data-dir 参数 ...

  2. Java RSA (SHA1withRSA)签名和验签

    static { try { SIGNATURE = Signature.getInstance("SHA1withRSA", "BC"); } catch ( ...

  3. qtp descriptive programming multiple language(多语言支持)

    so easy, 1,use the descriptive programming; 2,use the | chracter to seperate the different language ...

  4. 【shell】数据文件分割

    有时候我们必须把数据文件分割为更小的文件,这样方便我们邮件发送或者查看文件内容.split命令则可以用来分割文件. 一.根据大小来分割文件 1.一般分割 例如:现在有文件tmp.log,大小为:368 ...

  5. django之创建第6个项目-过滤器

    1.views.PY # Create your views here. #coding:utf-8 from django.http import HttpResponse import datet ...

  6. 【转】Jenkins怎么启动和停止服务

    笔者没有把Jenkins配置到tomcat中,每次都是用命令行来启动Jenkins.但是遇到一个问题:Jenkins一直是开着的,想关闭也关闭不了.百度了一些资料,均不靠谱(必须吐槽一下百度).于是进 ...

  7. shell笔记-算术运算

    算术运算在Bash shell环境中,可以利用let.(( ))和[]执行基本的算术操作.而在进行高级操作时,expr和bc这两个工具也会非常有用.let命令可以直接执行基本的算术操作.当使用let时 ...

  8. Navicatmysql专版

    Navicat是非常牛逼的公司在数据库可视化方面,做出许多工具...,可视化,为了更加人性化的操作,远离枯燥无味的黑色窗口 一睹真容 捐献一枚注册码 注册码:NAVH-WK6A-DMVK-DKW3 D ...

  9. C语言学习笔记 (010) - 编写strcpy函数

    很多公司的面试官在面试程序员的时候,要求应聘者写出库函数strcpy()的工作方式或者叫实现,很多人以为这个题目很简单,实则不然,别看这么一个小小的函数,它可以从三个方面来考查: (1)编程风格 (2 ...

  10. HDU 1710 Binary Tree Traversals (二叉树遍历)

    Binary Tree Traversals Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/O ...