显然,在 tarjan 的时候,假设遇到一个 \(dfn[u]\le low[v]\) 的节点,那么我们删去这个节点后一定会多出一个连通块,比如这样:
删去节点 \(5\) 后显然还剩下 \(3\) 个连通块,在这种情况下,我们看到节点 \(2,3\) 都满足上述条件,于是删去以后会多出来 \(2\) 个,加上原来的 \(1\) 个。
对于不是割点的,删去后一定还剩下一个。
于是我们就得出了以下结论
-
对于割点:每满足有一个 \(dfn[u]\le low[v]\) 的节点,删去后的连通块就多一个。
-
对于其他节点:删去后一定只剩一个。
于是我们又可以发现其实第二个结论根本没必要,不是割顶一定满足不了 \(dfn[u]\le low[v]\)。
最后因为要序号小的在前,我们直接用结构体存下来就好了。
Code:
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e4+5;
vector <int> G[maxn];
int dfn[maxn],low[maxn];
struct dat{
int w,id;
bool operator <(const dat& rhs)const{
if(w>rhs.w)return true;
if(w==rhs.w)return id<rhs.id;
return false;
}
}cnt[maxn];
int num;
void tarjan(int u,int pa){
dfn[u]=low[u]=++num;
int ch=0;
for(int i=0;i<G[u].size();++i){
int v=G[u][i];
if(!dfn[v]){
tarjan(v,u);
low[u]=min(low[u],low[v]);
if(dfn[u]<=low[v]){
if(++ch>1||u!=1)cnt[u].w++;
}
}
else if(v!=pa)low[u]=min(dfn[v],low[u]);
}
}
int main(){
int n,m;
while(cin>>n>>m&&n&&m){
int u,v;
memset(dfn,0,sizeof(dfn));
memset(cnt,0,sizeof(cnt));
for(int i=0;i<=n;++i){
G[i].clear();
cnt[i].id=i;
}
while(cin>>u>>v&&u>=0&&v>=0){
G[u].push_back(v);
G[v].push_back(u);
}
tarjan(1,0);
sort(cnt,cnt+n);
for(int i=0;i<m;++i)
cout<<cnt[i].id<<" "<<cnt[i].w+1<<endl;
cout<<endl;
}
return 0;
}