P3387 【模板】缩点
缩点+DAG上DP
首先求出图中的所有强连通分量,将每一个强连通分量缩成一点,保留连接两个不同的强联通分量的边构成新图。在新图上跑DAG上DP求最长路径即可。
AC代码:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int MAXN=100020;
int n,m,stot,fstw[MAXN],w[MAXN],u[MAXN],v[MAXN],from[MAXN],to[MAXN],ru[MAXN],fst[MAXN],nxt[MAXN];
int dfn[MAXN],cnt,low[MAXN],book[MAXN],sta[MAXN],top,b[MAXN],ctot,tsort[MAXN],t,dp[MAXN],ans;
void tarjan(int x)
{
dfn[x]=++cnt,low[x]=dfn[x],book[x]=1,sta[++top]=x;
for(int k=fst[x];k>0;k=nxt[k])
{
if(dfn[v[k]]&&book[v[k]])
//为什么要对在栈中的点进行标记:不标记的话你可能用以栈底为根的子树外的点的dfn/low值来更
//新自己的dfn/low值,显然这样是不合法的。
low[x]=min(low[x],dfn[v[k]]);
//遍历到祖先结点时,根据low数组的定义,用祖结点的dfs序更新low[x]
if(!dfn[v[k]])
{
tarjan(v[k]);
low[x]=min(low[x],low[v[k]]);
}
}
if(low[x]==dfn[x])
{
ctot++;
while(1)
{
b[sta[top]]=ctot,w[ctot]+=fstw[sta[top]],book[sta[top]]=0;
top--;
if(sta[top+1]==x) break;
}
}
}
void dfs(int x)
{
book[x]=1;
for(int k=fst[x];k>0;k=nxt[k])
if(!book[to[k]]) dfs(to[k]);
tsort[--t]=x;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&fstw[i]);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u[i],&v[i]);
nxt[i]=fst[u[i]],fst[u[i]]=i;
}
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i);
memset(fst,0,sizeof(fst));
memset(nxt,0,sizeof(nxt));
memset(book,0,sizeof(book));
for(int i=1;i<=m;i++)//建新边
{
if(b[u[i]]!=b[v[i]])
{
stot++,ru[b[v[i]]]++;
from[stot]=b[u[i]],to[stot]=b[v[i]],ru[to[stot]]++;
nxt[stot]=fst[from[stot]],fst[from[stot]]=stot;
}
}
t=ctot+1;
for(int i=1;i<=ctot;i++)//topsort
if(!ru[i]) dfs(i);
for(int i=1;i<=ctot;i++) dp[i]=w[i];
for(int i=1;i<=ctot;i++)//DAG上DP求最长路径
{
int x=tsort[i];
for(int k=fst[x];k>0;k=nxt[k]) dp[to[k]]=max(dp[to[k]],dp[x]+w[to[k]]);
}
for(int i=1;i<=ctot;i++) ans=max(ans,dp[i]);
cout<<ans;
return 0;
}