求强连通分量加缩点
不知道为什么在洛谷上交的求强连通分量过了在bzoj上交错了,于是(被迫)学了tarjan
#include <cstdio>
#include <cstring>
using namespace std;
struct node{
int a, b, n;
}d[50001];
int f[10001], h[10001], v[10001], du[10001], ans;
int find(int x){
return x == f[x]? x: f[x] = find(f[x]);
}
void dfs(int a){
int i, b;
for(i = h[a]; i; i = d[i].n){
b = d[i].b;
if(b) continue;
if(!v[b]){
v[b] = v[a] + 1;
dfs(b, a);
}
if(v[find(b)]>0){
if(v[f[b]] < v[find(a)]) f[f[a]] = f[b];
else f[f[b]] = f[a];
}
}
v[a] = -1;
}
int main(){
int i, j, n, m, a, b;
scanf("%d%d", &n, &m);
for(i = 1; i <= m; i++){
scanf("%d%d", &a, &b);
d[i].a = a; d[i].b = b; d[i].n = h[a]; h[a] = i;
}
for(i = 1; i <= n; i++) f[i] = i;
for(i = 1; i <= n; i++){
if(!v[i]){
v[i] = 1;
dfs(i, 0);
}
}
for(i = 1; i <= m; i++){
a = f[d[i].a], b = f[d[i].b];
if(f[a] == f[b]) continue;
du[a]++;
}
for(i = 1; i <= n; i++){
if(!du[f[i]]){
du[0]++;
du[f[i]]++;
a = f[i];
}
}
if(du[0] == 1){
for(i = 1; i <= n; i++){
if(f[i] == a) ans++;
}
printf("%d", ans);
}
else printf("0\n");
return 0;
}
上面是原来的,下面是tarjan的
学了tarjan,说一下tarjan的思路:
dfn[]为dfs序, low[]为返祖边会到的深度最小点
color[] 为强连通分量的编号, sum[]为此块强连通分量所包含结点个数
v[]为访问数组,v[] == 0为没访问过, v[] == 1为正在访问中, v[] == 2为已访问过
详看注释吧
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int stack[100001], cnt, h[10001], v[10001];
int low[10001], dfn[10001], tops, num;
int ans, color[10001], dfstime, p[10001];
int sum[10001];
struct node{
int a, b, n;
}d[100001], E[100001];
void dfs(int a){
int i, b;
low[a] = dfn[a] = ++dfstime;//dfn[]为dfs序, low[a]为返祖边会到的深度最小点
stack[++tops] = a;
v[a] = 1;
for(i = h[a]; i; i = d[i].n){
b = d[i].b;
if(!v[b]){
dfs(b);
low[a] = min(low[a], low[b]);//b 访问过了,有可能b有一条返祖边, 就把low[a]和 low[b]取min
}
else if(v[b] == 1){
low[a] = min(low[a], dfn[b]);//把low[a]更新为dfn[b] ,b在a上面
}
}
if(low[a] == dfn[a]){ //返祖点为自己的点
++cnt;
while(stack[tops] != a){
v[stack[tops]] = 2;
color[stack[tops--]] = cnt;
}
v[stack[tops]] = 2;
color[stack[tops--]] = cnt; //当前的结点还要弹出
}
}
int main(){
int i, j, n, m, a, b;
scanf("%d%d", &n, &m);
for(i = 1; i <= m; i++){
scanf("%d%d", &a, &b);
d[i].a = a; d[i].b = b; d[i].n = h[a];
h[a] = i;
}
for(i = 1; i <= n; i++){
if(!color[i]){
dfs(i);
}
}
for(i = 1; i <= n; i++) sum[color[i]]++;
for(i = 1; i <= m; i++){
a = d[i].a; b = d[i].b;
if(color[a] == color[b]) continue;
E[++num].a = color[a];
E[num].b = color[b];
E[num].n = p[color[a]];
p[color[a]] = num;//rebuild(), 连接两个连通分量
}
for(i = 1; i <= cnt; i++){
if(!p[i]){//出度为0
if(ans){
ans = 0;
break;
}
else ans = sum[i];
}
}
printf("%d\n", ans);
return 0;
}