hihocoder#1183 : 连通性一·割边与割点(tarjan)

题目:

描述

还记得上次小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);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值