P3388 【模板】割点(割顶)

博客围绕求解无向图割点展开。先给出题目,即求n个点、m条边无向图的割点。接着介绍割点概念,重点阐述Tarjan求割点算法,利用时间戳pre和low判断割点,还给出割点存在条件及更新规则,最后总结学习了该算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


前言

做完缩点做割点啊~


题目大意

给出一个 n n n 个点, m m m 条边的无向图,求图的割点。

对于全部数据, 1 ≤ n ≤ 2 × 1 0 4 1\leq n \le 2\times 10^4 1n2×104 1 ≤ m ≤ 1 × 1 0 5 1\leq m \le 1 \times 10^5 1m1×105


思路

先提一下什么是割点和桥。就是本来这个图或者这个图的一部分是联通的,然后拿走了一个点,这个图就不联通了,这个被拿走的点就是一个割点。桥就是把拿走的点换成拿走的边。

tarjan求割点算法

依然是利用时间戳, p r e pre pre 是原本的时间戳, l o w low low子树最低能到达的时间戳。

那么想一下,如果有一个点,它的 l o w [ v ] ≥ p r e [ u ] low[v]\geq pre[u] low[v]pre[u],也就是不管怎么走都只能走到 u u u 或者是 u u u 的子树,那么如果把 u u u 拿掉这个就断了。

即割点存在条件为之要有任意一个符合 l o w [ v ] ≥ p r e [ u ] low[v]\geq pre[u] low[v]pre[u] 的点, u u u 就是割点。

之后如果出边的点还是白点,就先递归那个点,然后用 l o w [ v ] low[v] low[v] 去更新 l o w [ u ] low[u] low[u]

如果不是白点,就用 p r e [ v ] pre[v] pre[v] 去更新 l o w [ u ] low[u] low[u]

特别要注意,如果 u u u 是根且只有一个孩子的话这个点也不是割点。


代码

#include <iostream>
using namespace std;
typedef long long ll;
const ll MAXM=2e5+5;
const ll MAXN=2e4+5;
struct edge{
    ll to,nxt;
}e[MAXM];
ll head[MAXN],tot;
void add(ll u,ll v){
    e[++tot].nxt=head[u];
    e[tot].to=v;
    head[u]=tot;
}
ll pre[MAXN],low[MAXN],dt,ans;
bool cut[MAXN];
void tarjan(ll u,ll father){
    pre[u]=low[u]=++dt;
    ll child=0;
    for (ll i = head[u]; i ; i=e[i].nxt) {
        ll v=e[i].to;
        if(pre[v]==0){
            child++;
            tarjan(v,u);
            low[u]= min(low[u],low[v]);
            if(low[v]>=pre[u]){
                cut[u]= true;
            }
        }else if(pre[v]<pre[u]&&v!=father){
            low[u]=min(low[u],pre[v]);
        }
    }
    if(father==0&&child==1){
        cut[u]= false;
    }
}
int main(){
    ll n,m;
    scanf("%lld%lld",&n,&m);
    for (int i = 1; i <=m ; ++i) {
        ll u,v;
        scanf("%lld%lld",&u,&v);
        add(u,v);
        add(v,u);
    }
    for (int i = 1; i <=n ; ++i) {
        if(!pre[i]){
            tarjan(i,0);
        }
    }
    for (int i = 1; i <=n ; ++i) {
        if(cut[i]){
            ans++;
        }
    }
    printf("%lld\n",ans);
    for (int i = 1; i <=n ; ++i) {
        if(cut[i]){
            printf("%d ",i);
        }
    }
    return 0;
}

总结

学习了tarjan的割点算法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值