题意
传送门 POJ 1236
题解
两次 D F S DFS DFS 进行强连通分量分解,并记录其拓扑序。遍历原图,对端点属于不同强连通分量的边在新图进行连边,就实现了将分解后的强连通分量缩成一个顶点,此时得到了一个 D A G DAG DAG(有向无环图)。
子任务 A A A 需要发送最少次数以遍历全部学校。对于入度为 0 0 0 的点,必须发送一次; D A G DAG DAG 中任一入度非 0 0 0 的节点,存在至少一个拓扑序小于这个节点且能到达这个节点的入度为 0 0 0 的节点;那么答案即 D A G DAG DAG 入度为 0 0 0 的节点数量。
子任务 B B B 需要添加最少数量的边,使原图变成一个强连通分量。考虑到图可能不连通,设图中存在 k k k 个 D A G DAG DAG,设第 i i i 个 D A G DAG DAG 入度为 0 0 0 的节点数为 n i n_i ni,出度为 0 0 0 的节点数为 m i m_i mi。需要将第 i i i 个 D A G DAG DAG 添加边使之变成两个强连通分量(即缩点后变成一条有向边及两端端点),最少需要 m a x ( n i , m i ) − 1 max(n_i,m_i)-1 max(ni,mi)−1 条边(考虑 m i m_i mi 为 2 2 2 的情况,设这两个节点分别为 u , v u,v u,v 则可以添加一条连接 u u u 以及可达 v v v 的入度为 0 0 0 的节点,添加一条连接 v v v 以及可达 u u u 的入度为 0 0 0 的节点,此时 u , v u,v u,v 相互可达;其他情况可用类似方法实现添边,得到强连通分量后删去所添的其中一条边,就得到了目标情况);最后添加 k k k 条边使整个图变成一个强连通分量。则答案为 ∑ i = 1 k m a x ( n i , m i ) \sum\limits_{i=1}^{k} max(n_i,m_i) i=1∑kmax(ni,mi)。
考虑到对于原本就为一个强连通分量的图,缩点后入度、出度为 0 0 0 的点是同一个节点,显然此时无需添加边,故特判之。对缩点得到的 D A G DAG DAG 按照有向边统计入度、出度,建图时按照无向图连边,那么 D F S DFS DFS 即可求解子任务 A , B A,B A,B。
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
#define maxn 105
int n, cmp[maxn], indeg[maxn], outdeg[maxn];
vector<int> G[maxn], rG[maxn], sG[maxn], vs;
bool used[maxn];
void add_edge(int u, int v)
{
G[u].push_back(v);
rG[v].push_back(u);
}
void dfs(int v)
{
used[v] = 1;
for (int i = 0; i < G[v].size(); i++)
{
int u = G[v][i];
if (!used[u]) dfs(u);
}
vs.push_back(v);
}
void rdfs(int v, int k)
{
used[v] = 1, cmp[v] = k;
for (int i = 0; i < rG[v].size(); i++)
{
int u = rG[v][i];
if (!used[u]) rdfs(u, k);
}
}
void sdfs(int v, int &n1, int &n2)
{
used[v] = 1;
n1 += !indeg[v] ? 1 : 0, n2 += !outdeg[v] ? 1 : 0;
for (int i = 0; i < sG[v].size(); i++)
{
int u = sG[v][i];
if (!used[u]) sdfs(u, n1, n2);
}
}
int main()
{
scanf("%d", &n);
for (int u = 0; u < n; u++)
{
int v;
while (~scanf("%d", &v) && v)
{
--v;
add_edge(u, v);
}
}
memset(used, 0, sizeof(used));
for (int u = 0; u < n; u++)
{
if (!used[u]) dfs(u);
}
memset(used, 0, sizeof(used));
int k = 0;
for (int i = vs.size() - 1; i >= 0; i--)
{
int u = vs[i];
if (!used[u]) rdfs(u, k++);
}
memset(indeg, 0, sizeof(indeg));
memset(outdeg, 0, sizeof(outdeg));
for (int u = 0; u < n; u++)
{
int c = cmp[u];
for (int i = 0; i < G[u].size(); i++)
{
int c2 = cmp[G[u][i]];
if (c == c2) continue;
sG[c].push_back(c2);
sG[c2].push_back(c);
++outdeg[c], ++indeg[c2];
}
}
memset(used, 0, sizeof(used));
int r1 = 0, r2 = 0;
for (int u = 0; u < k; u++)
{
int n1 = 0, n2 = 0;
if (!used[u])
{
sdfs(u, n1, n2);
}
r1 += n1, r2 += max(n1, n2);
}
printf("%d\n%d\n", r1, k == 1 ? 0 : r2);
return 0;
}