题目大意:给你一个有向图,让你找一条路径,使得该路径点权之和最大,如果重复经过只算一次。问最大是多少?
解题思路:对每个强连通分量,如果到了某一个点,必然要把这里的所有点走一遍,和才会最大(废话)。
于是我们缩点。
然后暴力、拓扑+DP貌似都行。
然后我Tarjan+暴力就过了(暴力出奇迹~\(≧▽≦)/~)
C++ Code:
#include<cstdio>
#include<cstring>
#include<cctype>
#include<vector>
#define N 100005
#define reg register
using namespace std;
struct edge{
int from,to,nxt;
}e[10*N<<1];
int head[N],n,m,low[N],dfn[N],cnt,stack[N],top,ltfl,ys[N],ans,f[N],a[N];
bool vis[N];
vector<int>v[N];
inline int readint(){
char c=getchar();
bool b=false;
for(;!isdigit(c);c=getchar())b=c=='-';
int d=0;
for(;isdigit(c);c=getchar())
d=(d<<3)+(d<<1)+(c^'0');
return b?-d:d;
}
inline int min(int a,int b){return a<b?a:b;}
inline int max(int a,int b){return a>b?a:b;}
void tarjan(int now){
dfn[now]=low[now]=++cnt;
vis[stack[++top]=now]=true;
for(reg int i=head[now];i;i=e[i].nxt)
if(!dfn[e[i].to]){
tarjan(e[i].to);
low[now]=min(low[now],low[e[i].to]);
}else
if(vis[e[i].to])low[now]=min(low[now],dfn[e[i].to]);
if(low[now]==dfn[now]){
int v;
f[++ltfl]=0;
do{
v=stack[top--];
::v[ltfl].push_back(v);
ys[v]=ltfl;
vis[v]=false;
f[ltfl]+=a[v];
}while(v!=now);
}
}
void dfs(int now,int s){
vis[now]=true;
for(reg int i=v[now].size()-1;i>=0;--i){
for(reg int j=head[v[now][i]];j;j=e[j].nxt)
if(!vis[ys[e[j].to]])dfs(ys[e[j].to],s+f[ys[e[j].to]]);
}
vis[now]=false;
ans=max(ans,s);
}
int main(){
ltfl=0;
memset(head,0,sizeof head);
n=readint(),m=readint();
for(reg int i=1;i<=n;++i)a[i]=readint();
for(reg int i=1;i<=m;++i){
int u=readint(),v=readint();
e[i]=(edge){u,v,head[u]};
head[u]=i;
}
memset(vis,0,sizeof vis);
top=0;
memset(low,0,sizeof low);
memset(dfn,0,sizeof dfn);
for(reg int i=1;i<=n;++i)
if(!dfn[i]){
tarjan(i);
}
memset(vis,0,sizeof vis);
ans=0;
for(reg int i=1;i<=ltfl;++i)
dfs(i,f[i]);
printf("%d\n",ans);
return 0;
}