题目描述:给你n个式子,有m种已知关系,问至少还需添加几种关系,使得这n个式子两两互证。
思路:DAG上的两个点能两两互达,说明是这个图的scc只有一个,那么我们可以重新构造这个图,先找出这个图的所有的scc,然后把每一个scc看作一个点,那么得到一个新的DAG图,那么问题就转化为,DAG上连数目最少的边,使得整个图所有的点能两两互达。显然这个答案是max(in0,out0)。in0代表图中入度为0的点,out0代表出度为0的点。为什么呢,对于一个图来说,它所有的点既有出边,又有入边,那么这个图必然所有点都互达,那么连接一条边,从图中in0到out0,就可以使得图中in0--,out0--,最少添加max(in0,out0)就使得in0 = 0,out0 = 0。注意,如果此图是一个scc,那么答案为0.
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
const int maxn = 2e4+10;
vector<int>G[maxn];
int pre[maxn],low[maxn],scc[maxn];
int dfs_clock,scc_cnt;
stack<int>s;
void dfs(int u)
{
pre[u] = low[u] = ++dfs_clock;
s.push(u);
for(int i=0;i<G[u].size();i++)
{
int v = G[u][i];
if(!pre[v])
{
dfs(v);
low[u] = min(low[u],low[v]);
}
else if(!scc[v])low[u] = min(low[u],pre[v]);
}
if(low[u] == pre[u])
{
++scc_cnt;
while(1)
{
int x = s.top();
s.pop();
scc[x] = scc_cnt;
if(x == u)break;
}
}
}
void find_scc(int n)
{
dfs_clock = scc_cnt = 0;
memset(pre,0,sizeof(pre));
memset(scc,0,sizeof(scc));
for(int i=1;i<=n;i++)
if(!pre[i])dfs(i);
}
int in[maxn],out[maxn];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)G[i].clear();
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
G[u].pb(v);
}
find_scc(n);
for(int i=1;i<=scc_cnt;i++)in[i] = out[i] = 1;
for(int u=1;u<=n;u++)
for(int i=0;i<G[u].size();i++)
{
int v = G[u][i];
if(scc[u]!=scc[v])in[scc[v]] = out[scc[u]] = 0;
}
int a=0,b=0;
for(int i=1;i<=scc_cnt;i++)
{
if(in[i])++a;
if(out[i])++b;
}
int ans = max(a,b);
if(scc_cnt == 1)ans = 0;
printf("%d\n",ans);
}
return 0;
}