[模板]强连通分量 - 缩点
题面描述
一个有向图,求出一条路径,可以经过一个点多次,求路径最大点权和。
Solution
使用tarjan查找强连通分量,缩点,重新建图,就得到一个DAG,且每个点为各自的强连通分量的点权和,跑一遍dp即可。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 10005
#define MAXM 100006
int u[MAXM],v[MAXM];
struct edge{
int v,next;
}G[MAXM];
int head[MAXN],vis[MAXN],dfn[MAXN],low[MAXN];
int color[MAXN],point[MAXN],size[MAXN];
int dp[MAXN];
int top = 0;int st[MAXN];
int num = 0;
int N,M;
int tot = 0;
inline void add(int u,int v){
G[++tot].v = v;G[tot].next = head[u];head[u] = tot;
}
void tarjan(int u){
color[u] = u;
dfn[u] = ++num; low[u] = dfn[u];
vis[u] = 1;
st[++top] = u;
for(register int i=head[u];i;i=G[i].next){
int v = G[i].v;
if(!dfn[v])tarjan(v);
if(vis[v])low[u] = std::min(low[u],low[v]);
}
if(low[u]==dfn[u]){
vis[u] = 0;
while(st[top]!=u){
color[st[top]] = u;
vis[st[top]] = 0;
top--;
}
top--;
}
}
int ans = 0;
int dfs(int u,int rt){
if(dp[u])return dp[u];
int maxx = 0;
for(register int i=head[u];i;i=G[i].next){
int v = G[i].v;
if(v==rt)continue;
int cur = dfs(v,u);
if(cur>maxx)maxx = cur;
}
return dp[u] = point[u]+maxx;
}
int score[MAXN];
int main(){
std::memset(head,0,sizeof(head));
std::memset(vis,0,sizeof(vis));
std::memset(color,0,sizeof(color));
std::memset(dfn,0,sizeof(dfn));
scanf("%d%d",&N,&M);
for(register int i=1;i<=N;++i)scanf("%d",&score[i]);
for(register int i=1;i<=M;++i){
scanf("%d%d",&u[i],&v[i]);
add(u[i],v[i]);
}
for(register int i=1;i<=N;++i){
if(!dfn[i])tarjan(i);
}
std::memset(head,0,sizeof(head));
std::memset(G,0,sizeof(G));
std::memset(size,0,sizeof(size));
for(register int i=1;i<=M;++i){
if(color[u[i]]==color[v[i]])continue;
add(color[u[i]],color[v[i]]);
size[color[v[i]]]++;
}
std::memset(point,0,sizeof(point));
std::memset(dp,0,sizeof(dp));
for(register int i=1;i<=N;++i){
point[color[i]]+=score[i];
}
for(register int i=1;i<=N;++i){
if(size[i]!=0)continue;
int cur = dfs(i,0);
if(cur>ans)ans = cur;
}
printf("%d",ans);
return 0;
}