[APIO2009]抢掠计划
题目描述
Siruseri 城中的道路都是单向的。不同的道路由路口连接。按照法律的规定, 在每个路口都设立了一个 Siruseri 银行的 ATM 取款机。令人奇怪的是,Siruseri 的酒吧也都设在路口,虽然并不是每个路口都设有酒吧。
Banditji 计划实施 Siruseri 有史以来最惊天动地的 ATM 抢劫。他将从市中心 出发,沿着单向道路行驶,抢劫所有他途径的 ATM 机,最终他将在一个酒吧庆 祝他的胜利。
使用高超的黑客技术,他获知了每个 ATM 机中可以掠取的现金数额。他希 望你帮助他计算从市中心出发最后到达某个酒吧时最多能抢劫的现金总数。他可 以经过同一路口或道路任意多次。但只要他抢劫过某个 ATM 机后,该 ATM 机 里面就不会再有钱了。 例如,假设该城中有 6 个路口,道路的连接情况如下图所示:
市中心在路口 1,由一个入口符号→来标识,那些有酒吧的路口用双圈来表
示。每个 ATM 机中可取的钱数标在了路口的上方。在这个例子中,Banditji 能抢 劫的现金总数为 47,实施的抢劫路线是:1-2-4-1-2-3-5。
输入输出格式
输入格式:
第一行包含两个整数 N、M。N 表示路口的个数,M 表示道路条数。接下来 M 行,每行两个整数,这两个整数都在 1 到 N 之间,第 i+1 行的两个整数表示第 i 条道路的起点和终点的路口编号。接下来 N 行,每行一个整数,按顺序表示每 个路口处的 ATM 机中的钱数。接下来一行包含两个整数 S、P,S 表示市中心的 编号,也就是出发的路口。P 表示酒吧数目。接下来的一行中有 P 个整数,表示 P 个有酒吧的路口的编号。
输出格式:
输出一个整数,表示 Banditji 从市中心开始到某个酒吧结束所能抢劫的最多 的现金总数。
输入输出样例
说明
50%的输入保证 N, M<=3000。所有的输入保证 N, M<=500000。每个 ATM 机中可取的钱数为一个非负整数且不超过 4000。
输入数据保证你可以从市中心 沿着 Siruseri 的单向的道路到达其中的至少一个酒吧。
分析:
本题所谓的路口事实上就是求强连通分量,然后再对所有强连通分量缩点,最后跑一边SPFA,求出答案即可。
CODE:
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #include <iostream> 5 #include <algorithm> 6 #include <queue> 7 #define M 1111111 8 using namespace std; 9 int n,m,S,p,T[M],Stack[M],dist[M],k,ans; 10 int nxt[M],head[M],to[M],tot,dfn[M],low[M],num[M],money[M],money_total[M],times; 11 int nxt2[M],head2[M],to2[M],tot2; 12 bool ins[M],inq[M]; 13 int min(int u,int v){return u<v?u:v;} 14 int max(int u,int v){return u>v?u:v;} 15 int read(){ 16 char c=getchar();int ans=0; 17 while (c<'0'||c>'9') c=getchar(); 18 while (c>='0'&&c<='9') ans=(ans<<1)+(ans<<3)+(c^48),c=getchar(); 19 return ans; 20 } 21 void add(int u,int v){ 22 nxt[++tot]=head[u];head[u]=tot;to[tot]=v; 23 return; 24 } 25 void add2(int u,int v){ 26 nxt2[++tot2]=head2[u];head2[u]=tot2;to2[tot2]=v; 27 return; 28 } 29 void tarjan(int x){ 30 Stack[++Stack[0]]=x;ins[x]=1; 31 dfn[x]=low[x]=++times; 32 int flag=Stack[0]; 33 for (int i=head[x];i;i=nxt[i]){ 34 if (!dfn[to[i]]) tarjan(to[i]),low[x]=min(low[x],low[to[i]]); 35 else if (ins[to[i]]) low[x]=min(low[x],dfn[to[i]]); 36 } 37 if (low[x]==dfn[x]){ 38 ++k; 39 for (int i=flag;i<=Stack[0];i++) ins[Stack[i]]=0,num[Stack[i]]=k,money_total[k]+=money[Stack[i]]; 40 Stack[0]=flag-1; 41 } 42 return; 43 } 44 void SPFA(){ 45 memset(dist,-1,sizeof(dist)); 46 dist[num[S]]=-money_total[num[S]]; 47 queue <int> Q; 48 Q.push(num[S]);inq[num[S]]=1; 49 while (!Q.empty()){ 50 int top=Q.front();Q.pop();inq[top]=0; 51 for (int i=head2[top];i;i=nxt2[i]) 52 if (dist[to2[i]]==-1||dist[to2[i]]>dist[top]-money_total[to2[i]]){ 53 dist[to2[i]]=dist[top]-money_total[to2[i]]; 54 if (!inq[to2[i]]) inq[to2[i]]=1,Q.push(to2[i]); 55 } 56 } 57 return; 58 } 59 int main(){ 60 n=read(),m=read(); 61 for (int i=1,u,v;i<=m;i++){ 62 u=read(),v=read(); 63 add(u,v); 64 } 65 for (int i=1;i<=n;i++) money[i]=read(); 66 S=read(),p=read(); 67 for (int i=1;i<=p;i++) T[i]=read(); 68 for (int i=1;i<=n;i++) 69 if (!dfn[i]) tarjan(i); 70 for (int i=1;i<=n;i++) 71 for (int j=head[i];j;j=nxt[j]) 72 if (num[i]!=num[to[j]]) add2(num[i],num[to[j]]); 73 SPFA(); 74 for (int i=1;i<=p;i++) ans=max(ans,-dist[num[T[i]]]); 75 printf("%d",ans); 76 return 0; 77 }