CF999E 通往首都的道路

1 CF999E 通往首都的道路

2 题目描述

\(Berland\) 有个 \(𝑛\) 城市和 \(𝑚\) 条路。每条路都连接着一对城市。\(Berland\) 的道路是单向的。
为了从首都出发能到达所有的城市,最少需要修建多少条新路?新路也是单向的。

3 题解

一开始看到这道题,我想的是 \(dfs\) 判断有几个点无法从根节点到达,然后直接输出个数。但如果单纯地这么做,会导致一些问题:如果一些无法从根节点到达的点形成了一个强连通分量,那么只需要连接根节点与其中的任意一个点,就可以从根节点到达该强连通分量中的所有其他点。考虑到强连通分量,这道题的思路其实就很明晰了:我们将所有的强连通分量缩成一个点,然后再找所有无法被根节点到达的点的个数即可。

这里的第二步实际上不能光跑 \(dfs\)。经过观察,我们发现:如果有一个节点有一条指向其他节点的单向边,且两个节点都无法从根节点到达,我们实际上只用连一条边,将根节点与指向其他节点的节点连接即可。我们这时发现:如果一个节点入度为 \(0\) 且不为根节点,那么它肯定需要连接,否则肯定不需要连接。这是因为,如果入度不为 \(0\),那么指向这个节点的节点有可能入度为 \(0\),如果入度仍不 为 \(0\),那么我们只要一直回溯,最终一定会找到两种节点:入度为 \(0\) 的节点或者根节点。由于我们将所有入度为 \(0\) 的节点都与根节点连接了,而且根节点自身肯定与自身连接,所有入度不为 \(0\) 的节点肯定可以从根节点到达。缩点采用 \(Tarjan\) 即可。

4 代码(空格警告):

#include <iostream>
#include <vector>
using namespace std;
const int N = 5010;
int n, m, s, u, v, cnt, tot, num, top, cnt2;
int ver[N], head[N], last[N], dfn[N], low[N];
int stack[N], ins[N], c[N];
int deg[N];
vector<int> scc[N];
void add(int x, int y)
{
    ver[++tot] = y;
    last[tot] = head[x];
    head[x] = tot;
}
void tarjan(int x)
{
    dfn[x] = low[x] = ++num;
    stack[++top] = x, ins[x] = 1;
    for (int i = head[x]; i; i = last[i])
    {
        int y = ver[i];
        if (!dfn[y])
        {
            tarjan(y);
            low[x] = min(low[x], low[y]);
        }
        else if (ins[y]) low[x] = min(low[x], dfn[y]);
    }
    if (dfn[x] == low[x])
    {
        int y;
        cnt++;
        do
        {
            y = stack[top--], ins[y] = 0;
            c[y] = cnt, scc[cnt].push_back(y);
        }while (x != y);
    }
}
int main()
{
    cin >> n >> m >> s;
    for (int i = 1; i <= m; i++)
    {
        cin >> u >> v;
        add(u, v);
    }
    for (int i = 1; i <= n; i++)
    {
        if (!dfn[i]) tarjan(i);
    }
    deg[c[s]] = 0x3f3f3f3f;
    for (int x = 1; x <= n; x++)
    {
        for (int i = head[x]; i; i = last[i])
        {
            int y = ver[i];
            if (c[x] == c[y]) continue;
            deg[c[y]]++;
        }
    }
    for (int i = 1; i <= cnt; i++)
    {
        if (!deg[i]) cnt2++;
    }
    cout << cnt2;
    return 0;
}

欢迎关注我的公众号:智子笔记

posted @ 2021-02-05 16:22  David24  阅读(95)  评论(0编辑  收藏  举报