CCF 201509-4 高速公路 传送门
这道题基本算是裸的Tarjan算法了, 只要求出每个强连通分量, 然后根据这些分量的元素数计算便利城市对即可, 数据也没有优化的坑. 会Tarjan算法的人大概会在考场上大呼简单, 但不会的人恐怕只有0分了, 说明CCF认证还是需要一定量的算法知识的.
第一稿只得了80分. 找了很多博客对比, 期间发现他们很多人, 一个简单的Tarjan算法写的那么复杂, 又乱又长, 无用的操作太多. 最后发现这个20分的错误是我算法的错误(题目还是裸的Tarjan算法). 在找到强连通分量时没有及时地标记出栈(in_stack[stack[top]] = false;
). 所以我此前的算法根本是错的, 能得80分也是运气.
100分代码
#include <iostream>
#include <vector>
using namespace std;
const int max_n = 10005;
vector<int> G[max_n];
int dfn[max_n], low[max_n], ans = 0;
int n, m, stack[max_n], top = 0, index = 0;
bool in_stack[max_n];
void tarjan(int u)
{
dfn[u] = low[u] = ++index;
stack[++top] = u;
in_stack[u] = true;
for (int i = 0; i < G[u].size(); ++i) {
int v = G[u][i];
if (!dfn[v]) { // 更新新的点.
tarjan(v);
low[u] = min(low[u], low[v]);
} else if (in_stack[v]) {
low[u] = min(low[u], low[v]);
} // 还剩下一种不在栈中但是已经访问过的情况,是其他连通分量的
}
if (dfn[u] == low[u]) {
int cnt = 0;
do {
++cnt;
in_stack[stack[top]] = false; // 我靠, 漏写了这一条.
} while (stack[--top + 1] != u);
ans += (cnt - 1)*cnt / 2;
}
}
int main()
{
cin >> n >> m;
for (int i = 0, u, v; i < m; ++i) {
cin >> u >> v;
G[u].push_back(v);
}
for (int i = 1; i <= n; ++i) {
if (!dfn[i]) tarjan(i);
}
cout << ans;
}