题目
https://www.luogu.org/problemnew/show/P3388
思路
复习tarjan
我们维护两个数组dfn[]和low[],dfn[u]表示顶点u第几个被(首次)访问,low[u]表示顶点u及其子树中的点,通过非父子边(回边),能够回溯到的最早的点(dfn最小)的dfn值(但不能通过连接u与其父节点的边)。对于边(u, v),如果low[v]>=dfn[u],此时u就是割点。
代码
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = 10007;
const int maxm = 100007;
struct node
{
int x,y;
}E[maxm],ne[maxm];
int n,m,tm = 0,top = 0,tot = 0;
int W[maxn],dfn[maxn],low[maxn],Head[maxn],Next[maxm],S[maxn],V[maxn],sd[maxn];
int nh[maxn],nn[maxm],in[maxn],D[maxn];
void tarjan(int x)
{
dfn[x] = low[x] = ++tm;
S[++top] = x; V[x] = 1;
for (int y,i = Head[x]; i; i = Next[i])
{
y = E[i].y;
if (!dfn[y])
{
tarjan(y);
low[x] = min(low[x],low[y]);
}
else if (V[y])
{
low[x] = min(low[x],low[y]);
}
}
if (dfn[x] == low[x])
{
int y;
while (y = S[top--])
{
V[y] = 0;
sd[y] = x;
if (y==x) break;
W[x] +=W[y];
}
}
return;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i = 1; i<=n; i++)
{
scanf("%d",&W[i]);
}
for (int i = 1; i<=m; i++)
{
scanf("%d%d",&E[i].x,&E[i].y);
Next[i] = Head[E[i].x];
Head[E[i].x] = i;
}
for (int i = 1; i<=n; i++)
{
if (!dfn[i])
{
tarjan(i);
}
}
for (int x,y,i = 1; i<=m; i++)
{
x = sd[E[i].x]; y = sd[E[i].y];
if (x!=y)
{
ne[++tot].x = x; ne[tot].y = y;
nn[tot] = nh[x];
nh[x] = tot;
in[y]++;
}
}
top = 0;
for (int i = 1; i<=n; i++)
{
if (sd[i] == i && !in[i])
{
S[++top] = i;
D[i] = W[i];
}
}
while (top)
{
int x = S[top--];
for (int y,i = nh[x]; i; i = nn[i])
{
y = ne[i].y;
D[y] = max(D[y],D[x]+W[y]);
if (--in[y] == 0)
{
S[++top] = y;
}
}
}
int ans = 0;
for (int i = 1; i<=n; i++)
{
ans = max(ans,D[i]);
}
cout<<ans<<endl;
return 0;
}