两道题除了加了个case数之外其余的都一样 HDU 3836 最近学习了一下Tarjan算法,虽然一些具体的细节还不是太懂,这道题就是给定一些集合之间的关系,判断需要加多少边才能成为强连通图。求出连通分量以后缩点,遍历所有边,如果边的端点属于两个联通分量,就把起点所在的连通分量出度++,终点的入度++,最后遍历所有连通块,统计所有分量的出度和入度,取较大的值即可。注意当连通分量数目为1时答案为0.
#include <map>
#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 20024;
const int maxm = 50002;
int n,m,ans,Index,scc,top,tot,sum;
int low[maxn],dfn[maxn],Stack[maxn],num[maxn],head[maxn],Belong[maxn],in[maxn],out[maxn];
bool Instack[maxn];
struct Edge {
int from,to,next;
Edge():from(0),to(0),next(0) {}
} edge[maxm];
void addedge(int u,int v) {
edge[tot].from=u;
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);
low[u]=min(low[u],low[v]);
} else if(Instack[v])
low[u]=min(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() {
memset(dfn,0,sizeof(dfn));
memset(Instack,false,sizeof(Instack));
memset(num,0,sizeof(num));
memset(low,0,sizeof(low));
memset(Belong,0,sizeof(Belong));
memset(in,0,sizeof(in));
ans=Index=top=scc=0;
for(int i=1; i<=n; i++)
if(!dfn[i])
Tarjan(i);
}
int main() {
int x,y;
while(~scanf("%d%d",&n,&m)) {
memset(head,-1,sizeof(head));
tot=0;
for(int i=0; i<m; i++) {
scanf("%d%d",&x,&y);
addedge(x,y);
}
solve();
memset(in, 0, sizeof(in));
memset(out,0,sizeof(out));
for(int i=0; i<tot; i++) {
int u=Belong[edge[i].from];
int v=Belong[edge[i].to];
if(u!=v) in[v]++,out[u]++;
}
ans=sum=0;
for(int i=1;i<=scc;++i) {
if(in[i]==0) ans++;
if(out[i]==0) sum++;
}
if(scc==1) printf("0\n");
else printf("%d\n", max(ans,sum));
}
return 0;
}