这题的正解——强连通分量+最短路,还是需要一定的想象力的。
首先,我们把所有能够互相到达的点(也就是强连通分量)进行缩点,缩完后的点的权值就是原先所有点的权值和。
然后,我们对缩完点的图重新建图,和强连通分量相连的点就是和属于这个强连通分量的原先节点相连的点。
最后,我们对新建的图做一遍最短路,然后每次读入一个节点,判断该节点所在的强连通分量到起点s的最短路是否大于当前的ans,若大于,则更新ans。
附上AC代码:
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <queue>
#define N 500010
using namespace std;
struct note{
int from,to,nt;
}s1[N],s2[N];
int n,m,x,y,w[N],num,h1[N],d[N],t[N],sy[N],a[N],sk[N],top,g,size,h2[N],s,q,dis[N],ans;
bool b[N];
queue <int> que;
void read(int& a){
static char c=getchar();a=0;int f=1;
while (!isdigit(c)) {if (c=='-') f=-1;c=getchar();}
while (isdigit(c)) a=a*10+c-'0',c=getchar();
a*=f;return;
}
void add1(int x,int y){
s1[num]=(note){x,y,h1[x]};
h1[x]=num++;
}
void so(int k){
b[k]=1,t[k]=d[k]=++size,sk[++top]=k;
for (int i=h1[k]; ~i; i=s1[i].nt)
if (!d[s1[i].to]) so(s1[i].to),t[k]=min(t[k],t[s1[i].to]);
else if (b[s1[i].to]) t[k]=min(t[k],d[s1[i].to]);
if (d[k]==t[k]){
++g;
while (1){
int p=sk[top--];b[p]=0,sy[p]=g,w[g]+=a[p];
if (p==k) break;
}
}
return;
}
void intt(){
read(n),read(m),memset(h1,-1,sizeof h1),memset(h2,-1,sizeof h2),num=0;
for (int i=1; i<=m; ++i) read(x),read(y),add1(x,y);
for (int i=1; i<=n; ++i) read(a[i]);
for (int i=1; i<=n; ++i) if (!d[i]) so(i);
}
void add2(int x,int y){
s2[num]=(note){x,y,h2[x]};
h2[x]=num++;
}
void build(){
num=0;
for (int i=1; i<=n; ++i)
for (int j=h1[i]; ~j; j=s1[j].nt)
if (sy[i]!=sy[s1[j].to]) add2(sy[i],sy[s1[j].to]);
}
void spfa(){
read(s),read(q),s=sy[s];
memset(dis,0,sizeof dis),memset(b,0,sizeof b);
que.push(s),b[s]=1,dis[s]=w[s];
while (!que.empty()){
int p=que.front();que.pop(),b[p]=0;
for (int i=h2[p]; ~i; i=s2[i].nt)
if (dis[s2[i].to]<dis[p]+w[s2[i].to]){
dis[s2[i].to]=dis[p]+w[s2[i].to];
if (!b[s2[i].to]){
b[s2[i].to]=1;
que.push(s2[i].to);
}
}
}
while (q--) read(x),x=sy[x],ans=max(ans,dis[x]==0x3f3f3f3f?0:dis[x]);
printf("%d",ans);
return;
}
int main(void){
intt();
build();
spfa();
return 0;
}