题目大意:
给定一个连通的无向图,一个点的“鸽子值”定义为将它从图中删去后连通块的个数。求按 ‘鸽子值’ 降序排列的前m个点。若鸽子值一样则按点标号升序排列。
题解:
在无向图中只有删除割点才会改变连通块的个数,删掉一个割点后连通块的个数等于包含有该割点的双连通分量的个数。
所以求一遍双连通分量,找出割点以及每个割点在的双连通分量的数量就行了。
关于连通分量
求点双连通及割点的板:
const int maxn = 2e6 + 5;
int n; //点数
int head[maxn],net[maxn],e[maxn],cnt; //邻接表
//BCC:无向图的点双连通分量和割点
int df,dfn[maxn],low[maxn],bccn[maxn],bc;
int isc[maxn];//是否为割点
vector<int> bcc[maxn];//双连通分量
stack<int> st;
void tarjan(int u,int pre){
dfn[u]=low[u]= ++df;
int child=0;
st.push(u);
for(int i=head[u];i;i=net[i]){
if(!dfn[e[i]]){
child++;
tarjan(e[i],u);
low[u]=min(low[u],low[e[i]]);
if(low[e[i]]>=dfn[u]){
isc[u]=1;
bc++;bcc[bc].clear();
bcc[bc].push_back(u);
while(bccn[e[i]]!=bc){
bcc[bc].push_back(st.top());
bccn[st.top()]=bc;
st.pop();
}
}
}
else if(dfn[e[i]]<dfn[u]&&e[i]!=pre){
low[u]=min(low[u],dfn[e[i]]);
}
}
if(pre==0&&child==1) isc[u]=0;
}
void find_bcc(){
df=bc=0;
for(int i=1;i<=n;++i) dfn[i]=low[i]=isc[i]=bccn[i]=0;
for(int i=1;i<=n;++i) if(!dfn[i]){
while(!st.empty()) st.pop();
tarjan(i,0);
}
}