割点

割点

含义之类的自行百度

标程

洛谷P3388

#include<iostream>
#include<cstring>
#include<cstdio>
#include<stack>
#include<algorithm>
#include<vector>
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 100005
#define minn -105
#define ll long long int
#define ull unsigned long long int
#define uint unsigned int
int n,m;
int fvis[maxn],low[maxn];
bool ans[maxn];
int index_(0);
int cur_fa;
vector<int>node[maxn];

void dfs(int cur)
{
    int num=0;
    index_++;
    low[cur]=fvis[cur]=index_;
    for(int i=0;i<node[cur].size();i++)
    {
        int nxt=node[cur][i];
        if(!low[nxt])
        {
            dfs(nxt);
            low[cur]=min(low[cur],low[nxt]);
            if(low[nxt]>=fvis[cur]&&cur!=cur_fa)ans[cur]=1;
            if(cur==cur_fa)num++;
        }
        else low[cur]=min(low[cur],fvis[nxt]); //本处是下面要讨论的地方
    }
    if(cur==cur_fa&&num>1)ans[cur]=1;
}

int main()
{
    cin>>n>>m;
    memset(low,0,sizeof(low));
    memset(ans,0,sizeof(ans));
    memset(fvis,0,sizeof(fvis));
    for(int i=0;i<m;i++)
    {
        int a,b;
        cin>>a>>b;
        node[a].push_back(b);
        node[b].push_back(a);
    }
    for(int i=1;i<=n;i++)
        if(!low[i])cur_fa=i,dfs(i);
    int sum=0;
    for(int i=1;i<=n;i++)
        if(ans[i])sum++;
    cout<<sum<<endl;
    for(int i=1;i<=n;i++)
        if(ans[i])cout<<i<<" ";
    cout<<endl;
    return 0;
}

讨论点

else low[cur]=min(low[cur],fvis[nxt]);

由于点与点联通的无向关系,那么在进行dfs的时候,我们尝试去遍历的nxt只能有两类情况

1)它未被dfs

2)它已经在dfs形成的栈中

第一种情况更新一定是更新成当前孩子的low,这与求scc(强连通分量)的思路类似

那么第二种情况中,为什么是更新成fvis(首次访问的时间),而不是low呢?

即:

else low[cur]=min(low[cur],low[nxt]);

请看下图

标号为遍历顺序

而现在让我们看看本来是割点的3号点是如何被忽视的吧

首先我们访问完了1234,

4,3无论按照上面何种方案,均会被改成1

我们继续遍历到7,这个时候问题就来了

到底标号7是取fvis[3]还是low[3]呢?

如果取low[3],又low[3]在前一过程中已经被更新成为了1

那么low[7]也就成了1

low[6],low[5]也跟着变成了1

这样一来割点3就被我们忽视了

那么核心毛病出在哪嘞?

在求强连通分量的过程中,我们上面两种写法都可以,对于scc而言,上面这种连圈型是一个强连通分量,从遍历角度上看,本质是一个圈,只不过中间有点交叉罢了

而对于研究割点问题而言,它研究的是极小圈,也就是说看的是最直接的那两个圈,我们找割点就是将他们能分离的点

两者有莫大的区别

posted @ 2020-06-21 13:04  et3_tsy  阅读(167)  评论(0编辑  收藏  举报