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;
}

浙公网安备 33010602011771号