求割点
vector<int>v[MAX_N];
int low[MAX_N],num[MAX_N],cnt;
bool iscut[MAX_N];
void dfs(int now,int fa){
low[now]=num[now]=++cnt;
int kid=0;
for(i=0;i<v[now].size();i++){
int to=v[now][i];
if(!num[to]){
kid++;
dfs(to,now);
low[now]=min(low[now],low[to]);
if(low[to]>=num[now]&&now!=1)
iscut[now]=true;
}
else if(num[to]<num[now]&&to!=fa)
low[now]=min(low[now],num[to]);
}
if(now==1&&kid>=2)
iscut[1]=true;
}
求割边把if(low[to]>=num[now]&&now!=1)改成if(low[to]>num[now]&&now!=1)即可。
P3469 [POI2008]BLO-Blockade
#include<iostream>
#include<cstdio>
#include<vector>
#include<stack>
using namespace std;
const int MAX_N=101000;
vector<int>v[MAX_N];
int low[MAX_N],num[MAX_N],cnt=0;
long long ans[MAX_N];
int n;
bool iscut[MAX_N];
int size[MAX_N];
stack<int>s;
void dfs(int now,int fa){
int i;
low[now]=num[now]=++cnt;
int kid=0;
size[now]=1;
int sum=1;
for(i=0;i<v[now].size();i++){
int to=v[now][i];
if(!num[to]){
kid++;
dfs(to,now);
low[now]=min(low[now],low[to]);
size[now]+=size[to];
if(low[to]>=num[now]){
iscut[now]=true;
sum+=size[to];
ans[now]+=(long long)size[to]*(n-size[to]);
}
}
else if(num[to]<num[now]&&to!=fa){
low[now]=min(low[now],num[to]);
}
}
if(now==1&&kid>=2)
iscut[1]=true;
if(iscut[now])
ans[now]+=n-1+(long long)(n-sum)*sum;
else
ans[now]=2*(n-1);
}
int main(void){
int m,i,a,b;
scanf("%d%d",&n,&m);
for(i=0;i<m;i++){
scanf("%d%d",&a,&b);
v[a].push_back(b);
v[b].push_back(a);
}
dfs(1,0);
for(i=1;i<=n;i++)
printf("%lld\n",ans[i]);
return 0;
}
关于求双连通分量
同样dfs求low数组,有多少个low值就有多少个边双联通分量。
const int MAX_N=10100;
int low[MAX_N],cnt;
int sum[MAX_N];
void dfs(int now,int fa){
int i;
low[now]=++cnt;
for(i=0;i<v[now].size();i++){
int to=v[now][i];
if(to==fa)
continue;
if(!low[to])
dfs(to,now);
low[now]=min(low[now],low[to]);
}
}
int tarjan(){//sum[i]即为low值为i的缩点的度数
int i;
memset(sum,0,sizeof(sum));
for(i=1;i<=n;i++){
for(j=0;j<v[i].size();j++){
if(low[i]!=low[v[i][j]])
sum[low[i]]++;
}
}
int ans=0;
for(i=1;i<=n;i++){
if(sum[i]==1)
ans++;
}//缩点数变成一个边双联通图需要添加的边数为(总度数为1的缩点个数+1)/2
return ans;
}