传送门
【题意】:
给一张图,每个点有一个ATM机,而其中的部分点是酒吧(可作为终点)。
抢掠计划就是从起点(1号点)出发,到终点的途中,将ATM机中的钱抢走。
问最多能抢到多少钱。
【注意】:
本题还以一种N<=1000000 的升级版,因为硬件原因(60万以上会爆栈),要写非递归的tarjan,在这里不展开。
【解法】:
因为是有向图,某些点又组成了环,又是让你求最长,综上,很清晰能够想到要用到强连通(不会的点这里)。
首先,有一些ATM机可以互相到达,那这些ATM机的钱可以在走完一次就拿走,这种操作其实就是缩点。
之后,我们重新建图(复杂度不高的),通过SPFA求出每一个点到达S点的最长距离V[i](单源最长路径)。
最后输出酒吧中到达S的V[i]最大的点(最长路)。
AC代码:不信的点这
#include <bits/stdc++.h>
using namespace std ;
const int N = 500000 + 10 ;
struct node{
int to,next ;
}G[2*N],edge[2*N];
queue <int> que ;
stack <int> st ;
int n,m,tot,tot1,tot2,a,b,cnt,x,s,p,ans=0 ;
int dfn[N],low[N],belong[N],v[N],f[N],cost[N],head[N],h[N] ;
bool instack[N],inqueue[N] ;
void add_edge(int a,int b){ //初始的建边
G[++tot1].to=b ;
G[tot1].next=head[a] ;
head[a]=tot1;
}
void add(int a,int b){
edge[++tot2].to=b ;
edge[tot2].next=h[a] ;
h[a]=tot2;
}
void tarjan(int now){ //求强连通分量
dfn[now]=low[now]=++tot ;
st.push(now) ;
instack[now]=true ;
for (int i=head[now];i;i=G[i].next) {
int to=G[i].to ;//这里写错一次
if (!dfn[to]){
tarjan(to) ;
low[now]=min(low[now],low[to]) ;
}
else {
if (instack[to]) low[now]=min(low[now],low[to]) ;
}
}
if (dfn[now]==low[now]) {
cnt++ ;//枚举强连通
while (st.top()!=now){
int t=st.top() ;
instack[t]=false ;
belong[t]=cnt ;//属于哪一个强连通分量
v[cnt]+=cost[t] ; //一个强连通分量合成一个点,因为相互可以到达,所以权值等于所有点的权值和
st.pop() ;
}
instack[now]=false ;
belong[now]=cnt ;
v[cnt]+=cost[now] ;
st.pop();
}
}
void new_(){
for (int i=1;i<=n;i++)
for (int j=head[i];j;j=G[j].next){
if (belong[i]!=belong[G[j].to]) { //属于不同的强连通,在它们之间建一条边(这里写错一次)
add(belong[i],belong[G[j].to]) ;
}
}
}
void spfa(){ //用SPFA求最长路
memset(inqueue,false,sizeof(inqueue)) ;
int start=belong[s] ;
que.push(start) ;
inqueue[start]=true ;
f[start]=v[start] ;//从起点到start的最长路是v[start]
while (!que.empty()){
int now=que.front() ;que.pop() ;
for(int i=h[now];i;i=edge[i].next){
int to=edge[i].to ;
if (f[now]+v[to]>f[to]) {
f[to]=f[now]+v[to] ;
if (!inqueue[to]) {
inqueue[to]=true ;
que.push(to) ;
}
}
}
inqueue[now]=false ;
}
}
int main(){
scanf("%d %d",&n,&m) ;
for (int i=1;i<=m;i++){
scanf("%d%d",&a,&b) ;
add_edge(a,b) ;
}
for (int i=1;i<=n;i++) scanf("%d",&cost[i]) ;//每个ATM机可抢到的钱
scanf("%d%d",&s,&p) ;//城市中心,酒吧个数
memset(dfn,0,sizeof(dfn)) ;
memset(instack,false,sizeof(instack)) ;
for (int i=1;i<=n;i++) if (!dfn[i]) tarjan(i) ;
new_() ;//缩点之后重新建图
spfa() ;//求一下最长路
for (int i=1;i<=p;i++){
scanf("%d",&x) ;
if (f[belong[x]]>ans) ans=f[belong[x]] ;
}
printf("%d\n",ans) ;
return 0 ;
}