LibreOJ - 10103电力 题解

因为题目没有保证整个图是联通的,我们不妨从中挑选一个联通分量去思考问题,对于一个联通分量而言,删掉一个点,有可能使联通块的个数增加,那么这个时候这个点就是割点。(定义源于 https://oi.wiki/graph/cut/
求割点我们可以对每个联通分量跑一遍Tarjan算法,那么我们在找到割点的时候应该怎么统计答案呢,首先,我们假设从节点\(a\)走到了节点\(u\),其中\(u\)是割点。
那么如果从\(u\)开始进行遍历去访问之前没有访问到的节点\(v\),一共有两种情况,第一种情况就是这个节点的\(low[v]<dfn[u]\),即\(v\)这个点出发可以接触到在\(u\)之前遍历到的节点,那么这个时候\(v\)这个点是不会对答案产生贡献的,因为\(v\)这个点和在\(u\)之前遍历到的节点联通,割掉\(u\)这个节点也不会产生新的联通块。第二种就是这个节点的\(low[v]\geq dfn[u]\),也就是说无论\(v\)怎么走都没有办法接触到在\(u\)之前遍历到的节点,那么割掉\(u\)这个节点之后一定会增加\(v\)节点所在的联通块。

总结一下,对于从\(u\)出发遍历之前没有遍历过的节点,如果满足\(low[v]\geq dfn[u]\),那么一定会让联通块的数量\(+1\),我们设满足上述条件的节点数量为\(res\),只需要在Tarjan过程当中顺便统计一下就可以,对于根节点而言,新增加的数目就是\(res\),如果不是根节点,那么新增加的数目是\(res+1\),因为要算上来到\(u\)的那个联通块。

#include <bits/stdc++.h>
using namespace std;
#define ll long long 


int n,m;
const int N=1e4+10;
int dfn[N],low[N],idx;
int ans=0;
vector<int> e[N];
bool vis[N];


void Tarjan(int u,int fa){
    vis[u]=true;
    low[u]=dfn[u]=++idx;
    int child=0;
    int res=0;
    for(int v:e[u]){
        if(!vis[v]){
            child++;
            Tarjan(v,u);
            low[u]=min(low[v],low[u]);
            if(fa!=u&&low[v]>=dfn[u]){
                res++;
            }
        }else if(v^fa){
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(fa^u) ans=max(ans,res+1);
    else if(fa==u){
        ans=max(ans,child);
    }
}


int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);


    while(cin>>n>>m){ 
        if(n==0&&m==0) return 0;
        for(int i=1;i<=n;i++){
            e[i].clear();
            vis[i]=false;
            dfn[i]=low[i]=0;
        }
        idx=0;
        ans=0;
        for(int i=1;i<=m;i++){
            int u,v;
            cin>>u>>v;
            u++,v++;
            e[u].push_back(v),e[v].push_back(u);
        }
        int number=0;
        for(int i=1;i<=n;i++){
            if(!vis[i]){
                number++;
                Tarjan(i,i);
            }
        }
        cout<<number-1+ans<<'\n';
    }
    return 0;
}
posted @ 2026-03-12 01:36  1445141  阅读(11)  评论(0)    收藏  举报