HDU3836 (Equivalent Sets)[强连通]

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3836

题目大意:给定一些命题,以及一部分命题的推导关系,问还需要至少添加几个推导关系能让所有的命题可以互相证明。

首先找出强连通分量,然后把每个强连通分量缩成一个点,得到一个DAG。接下来,设有a个结点(这里的结点对应于原图的一个强连通分量)的入度为0,b个结点的出度为0,则max{a,b}就是答案。注意特殊情况:当原图已经强连通时,答案是0而不是1。
引用自刘汝佳《算法竞赛入门经典 训练指南》

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN=20010;
const int MAXM=50010;
struct Edge {
int to,next;
}edge[MAXM];
int head[MAXN],tot;
int Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN];
int Index,top;
int scc;
bool Instack[MAXN];
int num[MAXN];

void addedge(int u,int v) {
edge[tot].to=v;edge[tot].next=head[u];head[u]=tot++;
}
void Tarjan(int u) {
int v;
Low[u]=DFN[u]=++Index;
Stack[top++]=u;
Instack[u]=true;
for(int i=head[u];i!=-1;i=edge[i].next) {
v=edge[i].to;
if(!DFN[v]) {
Tarjan(v);
if(Low[u]>Low[v]) Low[u]=Low[v];
}
else if(Instack[v] && Low[u]>DFN[v]) {
Low[u]=DFN[v];
}
}
if(Low[u]==DFN[u]) {
scc++;
do {
v=Stack[--top];
Instack[v]=false;
Belong[v]=scc;
num[scc]++;
}
while(v!=u);
}
}
void solve(int N) {
memset(DFN,0,sizeof(DFN));
memset(Instack,false,sizeof(Instack));
memset(num,0,sizeof(num));
memset(Belong,0,sizeof(Belong));
Index=scc=top=0;
for(int i=1;i<=N;i++) {
if(!DFN[i])
Tarjan(i);
}
}
void init() {
tot=0;
memset(head,-1,sizeof(head));
}
int n,m;
bool in0[MAXN];
bool out0[MAXN];
int main() {
int u,v;
while(~scanf("%d%d",&n,&m)){
init();
for(int i=0;i<m;i++) {
scanf("%d%d",&u,&v);
addedge(u,v);
}
solve(n);
for(int i=1;i<=n;i++) in0[i]=out0[i]=1;
for(int i=1;i<=n;i++) {
for(int j=head[i];j!=-1;j=edge[j].next) {
int v=edge[j].to;
if(Belong[i]!=Belong[v]) in0[Belong[v]]=out0[Belong[i]]=0;
}
}
int a=0,b=0;
for(int i=1;i<=scc;i++) {
if(in0[i]) a++;
if(out0[i]) b++;
}
if(scc==1) printf("0\n");
else printf("%d\n",max(a,b));
}
}