題目大意:
給定一個連通的無向圖,一個點的“鴿子值”定義爲將它從圖中刪去後連通塊的個數。求按 ‘鴿子值’ 降序排列的前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);
}
}