题目:
描述
还记得上次小Hi和小Ho学校被黑客攻击的事情么,那一次攻击最后造成了学校网络数据的丢失。为了避免再次出现这样的情况,学校决定对校园网络进行重新设计。
学校现在一共拥有N台服务器(编号1..N)以及M条连接,保证了任意两台服务器之间都能够通过连接直接或者间接的数据通讯。
当发生黑客攻击时,学校会立刻切断网络中的一条连接或是立刻关闭一台服务器,使得整个网络被隔离成两个独立的部分。
小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行:若干整数,用空格隔开,表示满足要求的服务器编号。从小到大排列。若没有满足要求的点,该行输出Null
第2..k行:每行2个整数,(u,v)表示满足要求的边。所有边根据u的大小排序,u小的排在前,当u相同时,v小的排在前面。若没有满足要求的边,则不输出
思路:
割点:
1:对根节点u,若其有两棵或两棵以上的子树,则该根结点u为割点;
2:对非叶子节点u(非根节点),若其中的某棵子树的节点均没有指向u的祖先节点的回边,说明删除u之后,根结点与该棵子树的节点不再连通;则节点u为割点。
割边:
当(u,v)为树边且low[v]>dfn[u]时,表示v节点只能通过该边(u,v)与u连通,那么(u,v)即为割边。
代码:
#include<cstdio>
#include<vector>
#include<algorithm>
#define maxn 23456
using namespace std;
int n,m,order=0;
int low[maxn],dfn[maxn];
int f[maxn],s[maxn];//父亲儿子
vector<int> edge[maxn];
vector<int> cut_point;
vector<pair<int,int> > cut_edge;
void tarjan(int u)
{
dfn[u]=low[u]=++order;
int flag=0;
for(int i=0;i<edge[u].size();i++)
{
int v=edge[u][i];
if(!dfn[v])
{
s[u]++;
f[v]=u;
tarjan(v);
if(low[v]>=dfn[u])
{
flag=1;
}
if(low[v]>dfn[u])
{
cut_edge.push_back(make_pair(min(v,u),max(v,u)));
}
low[u]=min(low[u],low[v]);
}
else if(v!=f[u])
{
low[u]=min(low[u],dfn[v]);
}
}
if(f[u]==0&&s[u]>1||(f[u]&&flag))
cut_point.push_back(u);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
edge[u].push_back(v);
edge[v].push_back(u);
}
tarjan(1);
sort(cut_point.begin(),cut_point.end());
sort(cut_edge.begin(),cut_edge.end());
if(cut_point.size()==0)
printf("Null\n");
else
{
printf("%d",cut_point[0]);
for(int i=1;i<cut_point.size();i++)
{
printf(" %d",cut_point[i]);
}
printf("\n");
for(int i=0;i<cut_edge.size();i++)
{
printf("%d %d\n",cut_edge[i].first,cut_edge[i].second);
}
}
}