题目大意
有 n n n 座城市,城市之间建立了 m m m 条有向的地下通道。
你需要发起若干轮轰炸,每轮可以轰炸任意多个城市。但每次轰炸的城市中,不能存在两个不同的城市 i i i, j j j 满足可以通过地道从城市 i i i 到达城市 j j j。
你需要求出最少需要多少轮可以对每座城市都进行至少一次轰炸。
数据范围
对于 20 % 20\% 20% 的数据, n , m ≤ 10 n,m\leq10 n,m≤10。
对于 40 % 40\% 40% 的数据, n , m ≤ 1000 n,m\leq1000 n,m≤1000。
对于另外 30 % 30\% 30% 的数据,保证无环。
对于 100 % 100\% 100% 的数据, n , m ≤ 1000000 n,m\leq1000000 n,m≤1000000。
思路
首先我们先来看无环的情况。
对于每一条链,我们都需要花费链的长度次来将链上面的所有节点炸掉,所以最终答案就是所有链的长度的最大值。
对于如何求所有链的长度的最大值,可以考虑用拓扑加 DP 来求。
我们再来考虑环。
容易想到缩点,缩完点后的图是 DAG,也就是上面的情况,此时答案应改为链的权值和,每个点的权值为这个点代表了原图中的几个点。
缩点可以用 Tarjan 求强连通分量来做。
具体实现可以看代码。
代码
#include <bits/stdc++.h>
using namespace std;
int n, m, Ecnt, last[1000005], dfn[1000005], Low[1000005], Time, size[1000005];
int u[1000005], v[1000005], fa[1000005], f[1000005], ans, rd[1000005], bz[100005];
stack <int> st;
struct Edge { int to, next; } E[1000005];
void addedge(int u, int v) { Ecnt++, E[Ecnt].to = v, E[Ecnt].next = last[u], last[u] = Ecnt; }
void dfs(int x) {
dfn[x] = Low[x] = ++Time, st.push(x);
for (int xy = last[x]; xy; xy = E[xy].next)
if (!dfn[E[xy].to])
dfs(E[xy].to), Low[x] = min(Low[x], Low[E[xy].to]);
else if (!bz[E[xy].to])
Low[x] = min(Low[x], dfn[E[xy].to]);
if (Low[x] == dfn[x]) {
while (st.top() != x)
size[x]++, fa[st.top()] = x, bz[st.top()] = 1, st.pop();
st.pop(), bz[x] = 1;
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
fa[i] = i, size[i] = 1;
for (int i = 1; i <= m; i++)
scanf("%d%d", &u[i], &v[i]), addedge(u[i], v[i]), rd[v[i]]++;
for (int i = 1; i <= n; i++)
if (!dfn[i])
dfs(i);
Ecnt = 0;
for (int i = 1; i <= n; i++)
last[i] = rd[i] = 0, f[i] = size[i];
for (int i = 1; i <= m; i++)
if (fa[u[i]] != fa[v[i]])
addedge(fa[u[i]], fa[v[i]]), rd[fa[v[i]]]++;
while (!st.empty())
st.pop();
for (int i = 1; i <= n; i++)
if (rd[i] == 0 && fa[i] == i)
st.push(i);
while (!st.empty()) {
int x = st.top();
st.pop();
for (int xy = last[x]; xy; xy = E[xy].next) {
rd[E[xy].to]--, f[E[xy].to] = max(f[E[xy].to], f[x] + size[E[xy].to]);
if (rd[E[xy].to] == 0)
rd[E[xy].to] = -1, st.push(E[xy].to);
}
}
for (int i = 1; i <= n; i++)
ans = max(ans, f[i]);
printf("%d", ans);
return 0;
}