我的思路是,先缩点成dag,然后分别计算能到起点(上游)和起点能到的点(下游)的点权和。在计算上游点的时候,如果它恰好能到下游点去,那么说明可以行程一个回路,于是更新一次ans
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 3e5, M = 5e5;
int e[M], h[N], hs[N], hr[N], nxt[M], idx = 1;
int n, m;
void add(int h[], int a, int b) { e[idx] = b, nxt[idx] = h[a], h[a] = idx++; }
int low[N], dfn[N], id[N], sz[N], stmp, scc;
int f[N], g[N];
bool st[N];
stack<int> s;
void tarjan(int u) {
dfn[u] = low[u] = ++stmp;
s.push(u), st[u] = true;
for (int i = h[u]; i; i = nxt[i]) {
int j = e[i];
if (!dfn[j]) {
tarjan(j);
low[u] = min(low[u], low[j]);
} else if (st[j])
low[u] = min(low[u], dfn[j]);
}
if (low[u] == dfn[u]) {
++scc;
int y;
do {
y = s.top();
s.pop(), st[y] = false;
sz[id[y] = scc]++;
} while (y != u);
}
}
int ans, start;
signed main() {
#ifndef ONLINE_JUDGE
freopen(".in", "r", stdin); // freopen("a.out", "w", stdout);
#else
cin.tie(0)->sync_with_stdio(0);
#endif
cin >> n >> m;
for (int i = 1; i <= m; ++i) {
int a, b;
cin >> a >> b;
add(h, a, b);
}
for (int i = 1; i <= n; ++i)
if (!dfn[i])
tarjan(i);
for (int u = 1; u <= n; ++u) {
int a = id[u];
for (int i = h[u]; i; i = nxt[i]) {
int j = e[i], b = id[j];
if (a != b)
add(hs, a, b), add(hr, b, a);
}
}
start = id[1];
ans = f[start] = sz[start];
for (int u = start; u; u--) {
for (int i = hs[u]; i; i = nxt[i]) {
int j = e[i];
f[j] = max(f[j], f[u] + sz[j]);
}
}
for (int u = start; u <= scc; ++u) {
for (int i = hr[u]; i; i = nxt[i]) {
int j = e[i];
f[j] = max(f[j], f[u] + sz[j]);
}
for (int i = hs[u]; i; i = nxt[i]) {
int j = e[i];
if (j <= start)
ans = max(ans, f[j] + f[u] - sz[start]);
}
}
cout << ans;
}