problem
给定一个 n n n 个点 m m m 条边的有向图,点带权,图中有若干个点是特殊点。
从给定的起点出发,以任意的特殊点为终点,求出路径上点权和的最大值(如果重复走过只算一次)。
数据范围: n , m ≤ 5 × 1 0 5 n, m\le5\times10^5 n,m≤5×105。
solution
Tarjan 缩完点后,拓扑排序时 d p dp dp 求最大点权和即可。注意一些细节问题。
时间复杂度 O ( n ) O(n) O(n)。
code
#include<bits/stdc++.h>
using namespace std;
namespace IO{
const int Rlen=1<<22|1;
char buf[Rlen],*p1,*p2;
inline char gc(){
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline T Read(){
char c=gc();T x=0,f=1;
while(!isdigit(c)) f^=(c=='-'),c=gc();
while( isdigit(c)) x=((x+(x<<2))<<1)+(c^48),c=gc();
return f?x:-x;
}
inline int in() {return Read<int>();}
}
using IO::in;
const int N=5e5+5;
int n,m,S,p,v[N],key[N];
struct edge{
int t,first[N],v[N],nxt[N];
void add(int x,int y) {nxt[++t]=first[x],first[x]=t,v[t]=y;}
}G,DAG;
struct edges{int u,v;}e[N];
int tot,top,scc,dfn[N],low[N],stk[N],instk[N],bel[N],val[N],flag[N];
void Max(int &x,int y) {if(x<y)x=y;}
void Min(int &x,int y) {if(x>y)x=y;}
void Tarjan(int x){
dfn[x]=low[x]=++tot;
stk[++top]=x,instk[x]=1;
for(int i=G.first[x];i;i=G.nxt[i]){
int to=G.v[i];
if(!dfn[to]) Tarjan(to),Min(low[x],low[to]);
else if(instk[to]) Min(low[x],dfn[to]);
}
int k;
if(dfn[x]==low[x]){
scc++;
do{
k=stk[top--],instk[k]=0;
bel[k]=scc,val[scc]+=v[k],flag[scc]|=key[k];
}while(k!=x);
}
}
queue<int>Q;
int ans,deg[N],f[N];
void Topsort(){
ans=val[bel[S]]*flag[bel[S]];
Q.push(bel[S]),f[bel[S]]=val[bel[S]];
while(!Q.empty()){
int x=Q.front();Q.pop();
for(int i=DAG.first[x];i;i=DAG.nxt[i]){
int to=DAG.v[i];
Max(f[to],f[x]+val[to]),Max(ans,f[to]*flag[to]);
if(!(--deg[to])) Q.push(to);
}
}
}
int main(){
n=in(),m=in();
for(int i=1;i<=m;++i){
e[i].u=in(),e[i].v=in(),G.add(e[i].u,e[i].v);
}
for(int i=1;i<=n;++i) v[i]=in();
S=in(),p=in();
while(p--) key[in()]=1;
Tarjan(S);
for(int i=1;i<=m;++i){
int u=e[i].u,v=e[i].v;
if(bel[u]&&bel[v]&&bel[u]!=bel[v]) DAG.add(bel[u],bel[v]),++deg[bel[v]];
}
Topsort();
printf("%d\n",ans);
return 0;
}