缩点
缩点,即把一张有向有环图中的 \(\rm SCC\) 都缩成一个个点,形成一个 \(\rm DAG\)。
对于本题,若一个点被选到了,则该点所在的 \(\rm SCC\) 中的所有点都可以选到,那当然都要选了。
老师:天上掉馅饼,我不选!
杠精:我偏不选!
所以,我们可以直接把一个 \(\rm SCC\) 缩成一个点,这个点的点权 \(sum(i)\) 为原 \(\rm SCC\) 内所有点的点权和。
首先是找 \(\rm SCC\),直接用 \(\rm Tarjan\) 即可。有向图的强连通分量
找到之后就将每个 \(\rm SCC\) 缩成一个点,建新图;然后使用 \(\rm dp\),对于新图上的边 \(<u,v>\),\(dp(v)\gets\max(dp(v),dp(u)+sum(v))\)。
注意到 \(\rm dp\) 是无后效性的,所以我们还需要进行一次拓扑排序。
\(\rm Code\)
#include <iostream>
#include <cstdio>
#include <stack>
#include <queue>
#define re register
using namespace std;
inline int read()
{
re int x = 0, f = 0;
re char c = getchar();
while (c < '0' || c > '9')
{
f |= c == '-';
c = getchar();
}
while (c >= '0' && c <= '9')
{
x = (x << 3) + (x << 1) + (c ^ '0');
c = getchar();
}
return f ? -x : x;
}
inline void write(int x)
{
if (x < 0)
{
putchar('-');
x = -x;
}
if (x > 9)
{
write(x / 10);
}
putchar(x % 10 ^ '0');
}
inline int max2(int x, int y)
{
return x > y ? x : y;
}
inline int min2(int x, int y)
{
return x < y ? x : y;
}
//-----------------------------------------------------------
const int MAXN = 1e4 + 5;
const int MAXM = 1e5 + 5;
int n, m, cnt1, cnt2, Time;
int w[MAXN], head1[MAXN], head2[MAXN], dfn[MAXN], low[MAXN], c[MAXN], sum[MAXN], in[MAXN], dp[MAXN];
bool ins[MAXN];
stack<int> sta;
queue<int> q;
struct edge
{
int to, nxt;
}e1[MAXM], e2[MAXM];
void add1(int u, int v)
{
e1[++cnt1] = edge{v, head1[u]};
head1[u] = cnt1;
}
void add2(int u, int v)
{
e2[++cnt2] = edge{v, head2[u]};
head2[u] = cnt2;
in[v]++;
}
void tarjan(int u)
{
dfn[u] = low[u] = ++Time;
sta.push(u);
ins[u] = true;
for (re int i = head1[u]; i; i = e1[i].nxt)
{
int v = e1[i].to;
if (!dfn[v])
{
tarjan(v);
low[u] = min2(low[u], low[v]);
}
else if (ins[v])
{
low[u] = min2(low[u], dfn[v]);
}
}
if (dfn[u] == low[u])
{
int v = 0;
while (u != v)
{
v = sta.top();
sta.pop();
ins[v] = false;
c[v] = u;
sum[u] += w[v];
}
}
}
void topo()
{
queue<int> q;
for (re int i = 1; i <= n; i++)
{
if (c[i] == i && !in[i]) //为SCC的起点
{
q.push(i);
dp[i] = sum[i];
}
}
while (!q.empty())
{
int u = q.front();
q.pop();
for (re int i = head2[u]; i; i = e2[i].nxt)
{
int v = e2[i].to;
dp[v] = max2(dp[v], dp[u] + sum[v]);
if (!--in[v])
{
q.push(v);
}
}
}
}
int main()
{
n = read(), m = read();
for (re int i = 1; i <= n; i++)
{
w[i] = read();
}
for (re int i = 1; i <= m; i++)
{
int u = read(), v = read();
add1(u, v);
}
for (re int i = 1; i <= n; i++)
{
if (!dfn[i])
{
tarjan(i);
}
}
for (re int u = 1; u <= n; u++)
{
for (int i = head1[u]; i; i = e1[i].nxt)
{
int v = e1[i].to;
if (c[u] != c[v]) //不在同一个SCC内,建边
{
add2(c[u], c[v]);
}
}
}
topo();
int ans = 0;
for (int i = 1; i <= n; i++)
{
ans = max2(ans, dp[i]);
}
write(ans);
return 0;
}