题目链接:https://vjudge.net/problem/UVALive-4287

题意:有n个命题,已知其中的m个推导,要证明n个命题全部等价(等价具有传递性),最少还需要做出几次

推导

解法:由已知的推导可以建一张无向图,则问题变成了最少需要增加几条边能使图变成强连通图。找出所有的

强连通分量,将每一个连通分量视作一个大节点,则整张图变成了一张DAG。设出度为0的大节点个数为a,

入度为0的大节点个数为b,则答案就是max(a,b)。为什么是这样呢?因为要使等价证明前进下去,每个大节

点的出度和入度都必须不能是0,注意特判整个图是强连通分量的情况

///UVALIVE 4287

#include <bits/stdc++.h>
using namespace std;
const int maxn = 20005;
int n, m, low[maxn], dfn[maxn], dfs_clk, scc, top;
int in[maxn], out[maxn], s[maxn], belong[maxn], head[maxn], edgecnt;
bool inq[maxn];
struct edge{
    int v, nxt;
    edge(){}
}E[maxn*4];
void init(){
    memset(head, -1, sizeof(head));
    edgecnt=0;
}
void add(int u, int v){
    E[edgecnt].v = v, E[edgecnt].nxt = head[u], head[u] = edgecnt++;
}
void tarjan(int x){
    int now=0;
    dfn[x]=low[x]=++dfs_clk;
    s[++top]=x; inq[x]=1;
    for(int i=head[x]; i+1; i=E[i].nxt){
        int to=E[i].v;
        if(!dfn[to]){
            tarjan(to);
            low[x]=min(low[x],low[to]);
        }
        else if(inq[to]){
            low[x]=min(low[x],dfn[to]);
        }
    }
    if(low[x]==dfn[x]){
        ++scc;
        while(now!=x){
            now=s[top];top--;
            belong[now]=scc;
            inq[now]=0;
        }
    }
}

int main(){
    int T; scanf("%d", &T);
    while(T--){
        scanf("%d%d", &n,&m);
        init();
        while(m--){
            int u, v;
            scanf("%d%d", &u,&v);
            add(u, v);
        }
        memset(inq, 0, sizeof(inq));
        memset(low, 0, sizeof(low));
        memset(dfn, 0, sizeof(dfn));
        memset(belong, 0, sizeof(belong));
        memset(in, 0, sizeof(in));
        memset(out, 0, sizeof(out));
        memset(s, 0, sizeof(s));
        dfs_clk = scc = top = 0;
        for(int i=1; i<=n; i++){
            if(!dfn[i]){
                tarjan(i);
            }
        }
        for(int i=1; i<=scc; i++){
            in[i] = out[i] = 0;
        }
        int a = 0, b = 0;
        for(int i=1; i<=n; i++){
            for(int j=head[i]; j+1; j=E[j].nxt){
                int v = E[j].v;
                if(belong[i]!=belong[v]){
                    out[belong[i]]=in[belong[v]]=1;
                }
            }
        }
        for(int i=1; i<=scc; i++){
            if(!in[i]) a++;
            if(!out[i]) b++;
        }
        int ans = max(a, b);
        if(scc==1){
            ans=0;
        }
        printf("%d\n", ans);
    }
    return 0;
}