【CCF】201509-4 高速公路(有向图+强连通分量)

试题编号: 201509-4
试题名称: 高速公路
时间限制: 1.0s
内存限制: 256.0MB
问题描述:
问题描述
  某国有n个城市,为了使得城市间的交通更便利,该国国王打算在城市之间修一些高速公路,由于经费限制,国王打算第一阶段先在部分城市之间修一些单向的高速公路。
  现在,大臣们帮国王拟了一个修高速公路的计划。看了计划后,国王发现,有些城市之间可以通过高速公路直接(不经过其他城市)或间接(经过一个或多个其他城市)到达,而有的却不能。如果城市A可以通过高速公路到达城市B,而且城市B也可以通过高速公路到达城市A,则这两个城市被称为便利城市对。
  国王想知道,在大臣们给他的计划中,有多少个便利城市对。
输入格式
  输入的第一行包含两个整数n, m,分别表示城市和单向高速公路的数量。
  接下来m行,每行两个整数a, b,表示城市a有一条单向的高速公路连向城市b。
输出格式
  输出一行,包含一个整数,表示便利城市对的数量。
样例输入
5 5
1 2
2 3
3 4
4 2
3 5
样例输出
3
样例说明

城市间的连接如图所示。有3个便利城市对,它们分别是(2, 3), (2, 4), (3, 4),请注意(2, 3)和(3, 2)看成同一个便利城市对。
评测用例规模与约定
  前30%的评测用例满足1 ≤ n ≤ 100, 1 ≤ m ≤ 1000;
  前60%的评测用例满足1 ≤ n ≤ 1000, 1 ≤ m ≤ 10000;
  所有评测用例满足1 ≤ n ≤ 10000, 1 ≤ m ≤ 100000。

强连通分量Tarjan算法
https://blog.csdn.net/qq_43294914/article/details/103324836

#include <cstdio>
#include <algorithm>
#include <string.h>
using namespace std;
const int N = 1e6 + 1;
struct node
{
    int v, next;
} edge[N];
int sum[N];
int dfn[N], low[N];
int stack[N], h[N], visit[N], cnt, tot, idx;
void add(int x, int y)
{
    edge[++cnt].next = h[x];
    edge[cnt].v = y;
    h[x] = cnt;
}

void tarjan(int x) //代表第几个点在处理。递归的是点。
{
    dfn[x] = low[x] = ++tot; // 新进点的初始化。
    stack[++idx] = x;        //入栈
    visit[x] = 1;            //表示在栈里
    for (int i = h[x]; i != -1; i = edge[i].next)
    {
        if (!dfn[edge[i].v])
        {                                         //如果没访问过
            tarjan(edge[i].v);                    //往下进行延伸,开始递归
            low[x] = min(low[x], low[edge[i].v]); //递归出来,比较谁是谁的儿子/父亲,就是树的对应关系,涉及到强连通分量子树最小根的事情。
        }
        else if (visit[edge[i].v])
        {                                         //如果访问过,并且还在栈里。
            low[x] = min(low[x], dfn[edge[i].v]); //比较谁是谁的儿子/父亲。就是链接对应关系
        }
    }
    if (low[x] == dfn[x]) //发现是整个强连通分量子树里的最小根。
    {
        do
        {
            sum[x]++;
            visit[stack[idx]] = 0;
            idx--;
        } while (x != stack[idx + 1]); //出栈,并且输出。
    }
    return;
}

int main()
{
    memset(h, -1, sizeof h);
    int n, m;
    scanf("%d%d", &n, &m);
    int x, y;
    for (int i = 1; i <= m; i++)
    {
        scanf("%d%d", &x, &y);
        add(x, y);
    }
    for (int i = 1; i <= n; i++)
        if (!dfn[i])
            tarjan(i); //当这个点没有访问过,就从此点开始。防止图没走完

    int ans = 0;
    for (int i = 1; i <= n; i++)
    {
        if (sum[i])
        {
            ans += (sum[i] - 1) * sum[i] / 2;
        }
    }

    printf("%d\n", ans);
    return 0;
}
发布了84 篇原创文章 · 获赞 12 · 访问量 2915

猜你喜欢

转载自blog.csdn.net/qq_43294914/article/details/103325000