1179: [Apio2009]Atm
Time Limit: 15 Sec Memory Limit: 162 MB
Submit: 4548 Solved: 2024
Description
Siruseri 城中的道路都是单向的。不同的道路由路口连接。按照法律的规定, 在每个路口都设立了一个 Siruser
i 银行的 ATM 取款机。令人奇怪的是,Siruseri 的酒吧也都设在路口,虽然并不是每个路口都设有酒吧。Bandit
ji 计划实施 Siruseri 有史以来最惊天动地的 ATM 抢劫。他将从市中心 出发,沿着单向道路行驶,抢劫所有他
途径的 ATM 机,最终他将在一个酒吧庆 祝他的胜利。使用高超的黑客技术,他获知了每个 ATM 机中可以掠取的
现金数额。他希 望你帮助他计算从市中心出发最后到达某个酒吧时最多能抢劫的现金总数。他可 以经过同一路口
或道路任意多次。但只要他抢劫过某个 ATM 机后,该 ATM 机 里面就不会再有钱了。 例如,假设该城中有 6 个
路口,道路的连接情况如下图所示:
市中心在路口 1,由一个入口符号→来标识,那些有酒吧的路口用双圈来表示。每个 ATM 机中可取的钱数标在了
路口的上方。在这个例子中,Banditji 能抢 劫的现金总数为 47,实施的抢劫路线是:1-2-4-1-2-3-5。
Input
第一行包含两个整数N、M。N表示路口的个数,M表示道路条数。
接下来M行,每行两个整数,这两个整数都在1到N之间,
第i+1行的两个整数表示第i条道路的起点和终点的路口编号。
接下来N行,每行一个整数,按顺序表示每个路口处的ATM机中的钱数。
接下来一行包含两个整数S、P,S表示市中心的编号,也就是出发的路口。P表示酒吧数目。
接下来的一行中有P个整数,表示P个有酒吧的路口的编号
N, M<=500000。每个ATM机中可取的钱数为一个非负整数且不超过4000。
输入数据保证你可以从市中心沿着Siruseri的单向的道路到达其中的至少一个酒吧。
Output
输出一个整数,表示Banditji从市中心开始到某个酒吧结束所能抢劫的最多的现金总数。
Sample Input
6 7
1 2
2 3
3 5
2 4
4 1
2 6
6 5
10
12
8
16
1 5
1 4
4
3
5
6
Sample Output
47
解析:
因为每条路径可重复走多次,所以世界Tarjan缩点后跑SPFA求最长路即为答案。路径长度为终点集合的点权和。
代码:
#include <bits/stdc++.h>
using namespace std;
const int Max=500010;
int n,m,size,s,ans,cnt,tot,Index,q;
int first[Max],First[Max],dis[Max],num[Max],low[Max],father[Max],sum[Max],a[Max],p[Max],v[Max];
struct shu{int to,next,len;};
shu edge[Max<<1];
struct tree{int to,next;};
tree Edge[Max<<1];
inline int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') {f=-1;c=getchar();}
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
inline void Build(int x,int y)
{
Edge[++size].next=first[x];
first[x]=size;
Edge[size].to=y;
}
inline void build(int x,int y,int z)
{
edge[++size].next=first[x];
first[x]=size;
edge[size].to=y;
edge[size].len=z;
}
inline void tarjan(int point)
{
low[point] = num[point] = ++Index;
p[++tot]=point;
v[point]=1;
for(int u=first[point];u;u=Edge[u].next)
{
if(!num[Edge[u].to]) {tarjan(Edge[u].to);low[point]=min(low[Edge[u].to],low[point]);}
if(v[Edge[u].to]) low[point]=min(num[Edge[u].to],low[point]);
}
if(low[point]==num[point])
{
cnt++;
while(1)
{
int x=p[tot--];
v[x]=0;
father[x]=cnt;
sum[cnt] += a[x];
if(x==point) break;
}
}
}
inline void SPFA()
{
memset(p,0,sizeof(p));
memset(dis,0,sizeof(dis));
int head=0,tail=1;
p[1]=father[s];
dis[father[s]]=sum[father[s]];
while(head < tail)
{
int point=p[++head];
v[p[head]]=0;
for(int u=first[point];u;u=edge[u].next)
{
int to=edge[u].to;
if(dis[to] < dis[point]+edge[u].len)
{
dis[edge[u].to]=dis[point]+edge[u].len;
if(!v[to])
{
v[to]=1;
p[++tail]=to;
}
}
}
}
}
inline int mx(int x,int y){return x > y ? x : y;}
int main()
{
n=get_int(),m=get_int();
for(int i=1;i<=m;i++)
{
int x=get_int(),y=get_int();
Build(x,y);
}
for(int i=1;i<=n;i++) a[i] = get_int();
for(int i=1;i<=n;i++) if(!num[i]) tarjan(i);
s=get_int(),q=get_int(),size=0;
memcpy(First,first,sizeof(first));
memset(first,0,sizeof(first));
for(int i=1;i<=n;i++)
for(int u=First[i];u;u=Edge[u].next)
if(father[i] != father[Edge[u].to])
{
int to=Edge[u].to;
build(father[i] , father[to] , sum[father[to]]);
}
SPFA();
for(int i=1;i<=q;i++) {int x=get_int();ans = mx(ans ,dis[father[x]]);}
cout<<ans<<"\n";
return 0;
}