给定一个 nn 个点 mm 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。
前置知识
没学过的同学可以学一下。
分析
我们可以先用强联通分量对图进行缩点,那么现在,这张图就是 了。我们再拓扑排序,然后就去 。
是这样的:
我们用 表示以第 个点结尾的最优路径,这个显然很好转移。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
template<typename T>inline void write(T x){
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar('0'+x%10);
}
const int MAXN=1e6+10,MAXM=1e6+10;
int s[MAXN],stop,dfn[MAXN],low[MAXN],scccnt,sccnum[MAXN],dfscnt,tot,he[MAXN],ne[MAXM<<1],ed[MAXM<<1],n,x,y,ds[MAXN],de[MAXN],m,dis[MAXN],tp,ans[MAXN],sum,f[MAXN];
queue<int>q;
vector<int>rd[MAXN];
vector<int>cd[MAXN];
void add(int x,int y){
ed[++tot]=y;
ne[tot]=he[x];
he[x]=tot;
}
void tuopu(){
for(int i=1;i<=scccnt;i++)
if(!de[i])q.push(i);
while(q.size()){
int u=q.front();
q.pop();
ans[++tp]=u;
for(auto i:cd[u])
if(--de[i]==0)q.push(i);
}
}
inline void tarjan(int now){
dfn[now]=low[now]=++dfscnt;
s[stop++]=now;
for(int i=he[now];i;i=ne[i]){
if(!dfn[ed[i]]){
tarjan(ed[i]);
low[now]=min(low[now],low[ed[i]]);
}else if(!sccnum[ed[i]]){
low[now]=min(low[now],dfn[ed[i]]);
}
}
if(dfn[now]==low[now]){
scccnt++;
do{
sccnum[s[--stop]]=scccnt;
ds[scccnt]+=dis[s[stop]];
}while(s[stop]!=now);
}
}
int main(){
read(n);read(m);
for(int i=1;i<=n;i++)read(dis[i]);
for(int i=1;i<=m;i++){
read(x);read(y);
add(x,y);
}
for(int i=1;i<=n;i++)
if(!dfn[i])tarjan(i);
for(int i=1;i<=n;i++)
for(int j=he[i];j;j=ne[j])
if(sccnum[i]!=sccnum[ed[j]]){
de[sccnum[ed[j]]]++;
cd[sccnum[i]].push_back(sccnum[ed[j]]);
rd[sccnum[ed[j]]].push_back(sccnum[i]);
}
tuopu();
for(int i=1;i<=tp;i++){
f[ans[i]]=ds[ans[i]];
for(auto j:rd[ans[i]])
f[ans[i]]=max(f[ans[i]],f[j]+ds[ans[i]]);
}
for(int i=1;i<=scccnt;i++)sum=max(f[i],sum);
cout<<sum;
return 0;
}