传送门:洛谷-【模板】缩点
题意
给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。
数据范围
n<=10^4,m<=10^5,点权<=1000
题解
本蒟蒻学习Tarjan的板题,DP一下即A。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10+1e5;
const int M=1e5+10+1e4;
int head[N],to[M],nxt[M];
int n,m,tot,sum,cnt,vis[N];
int sta[N],top,in[N],ss[N];
int val[N],dp[M],ans,dfn[N],low[N];
int x[N],y[N];
inline int read(){
char ch=getchar();int x=0,t=1;
while(ch<'0' || ch>'9'){if(ch=='-')t=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*t;
}
inline void lk(int u,int v)
{
to[++tot]=v;nxt[tot]=head[u];head[u]=tot;
}
inline void search(int x)
{
if(dp[x]) return;
dp[x]=ss[x];
int maxsum=0;
for(int i=head[x];i;i=nxt[i]){
int e=to[i];
if(!dp[e]) search(e);
maxsum=max(maxsum,dp[e]);
}
dp[x]+=maxsum;
}
inline void tarjan(int x)
{
dfn[x]=++cnt;low[x]=cnt;
sta[++top]=x;vis[x]=1;
for(int i=head[x];i;i=nxt[i]){
int e=to[i];
if(!dfn[e]){
tarjan(e);
low[x]=min(low[e],low[x]);
}else if(vis[e]){
low[x]=min(dfn[e],low[x]);
}
}
if(dfn[x]==low[x]){
sum++;
while(sta[top+1]!=x){
in[sta[top]]=sum;
ss[sum]+=val[sta[top]];
vis[sta[top--]]=0;
}
}
}
int main(){
n=read();m=read();
for(int i=1;i<=n;i++) val[i]=read();
for(int i=1;i<=m;i++){
x[i]=read(),y[i]=read();
lk(x[i],y[i]);
}
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i);
memset(head,0,sizeof(head));
memset(nxt,0,sizeof(nxt));
memset(to,0,sizeof(to));
tot=0;
for(int i=1;i<=m;i++){
if(in[x[i]]!=in[y[i]]){
lk(in[x[i]],in[y[i]]);
}
}
for(int i=1;i<=sum;i++){
if(!dp[i]){
search(i);
ans=max(ans,dp[i]);
}
}
printf("%d\n",ans);
return 0;
}