题解 P3627 [APIO2009]抢掠计划

Siruseri 城中的道路都是单向的。不同的道路由路口连接。按照法律的规定,在每个路口都设立了一个 Siruseri 银行的 ATM 取款机。令人奇怪的是,Siruseri 的酒吧也都设在路口,虽然并不是每个路口都设有酒吧。

Banditji 计划实施 Siruseri 有史以来最惊天动地的 ATM 抢劫。他将从市中心出发,沿着单向道路行驶,抢劫所有他途径的 ATM 机,最终他将在一个酒吧庆祝他的胜利。

使用高超的黑客技术,他获知了每个 ATM 机中可以掠取的现金数额。他希望你帮助他计算从市中心出发最后到达某个酒吧时最多能抢劫的现金总数。他可以经过同一路口或道路任意多次。但只要他抢劫过某个 ATM 机后,该 ATM 机里面就不会再有钱了。 例如,假设该城中有 66 个路口,道路的连接情况如下图所示:

市中心在路口 \(1\),由一个入口符号 → 来标识,那些有酒吧的路口用双圈来表示。每个 ATM 机中可取的钱数标在了路口的上方。在这个例子中,Banditji 能抢劫的现金总数为 \(47\),实施的抢劫路线是:\(1-2-4-1-2-3-5\)

前置知识:

首先自然就是缩点,图变成了 \(DAG\)。我们之后就可以开始跑最长路了。

讲讲缩点完的建图:

以样例为例

在这里插入图片描述

当然起点也需要权值。

代码:

#include <bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &FF){
	T RR=1;FF=0;char CH=getchar();
	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
	FF*=RR;
}
template<typename T>inline void write(T x){
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar('0'+x%10);
}
const int MAXN=1e6+10,MAXM=1e6+10;
int s[MAXN],stop,dfn[MAXN],low[MAXN],scccnt,sccnum[MAXN],dfscnt,tot,he[MAXN],ne[MAXM<<1],ed[MAXM<<1],n,m,x,y,S,P,r[MAXN],money[MAXN],mny[MAXN],ans;
bool u[MAXN];
vector<pair<int,int> >v[MAXN];
void add(int x,int y){
	ed[++tot]=y;
	ne[tot]=he[x];
	he[x]=tot;
}
inline void tarjan(int now){
	dfn[now]=low[now]=++dfscnt;
	s[stop++]=now;
	for (int i=he[now];i;i=ne[i]){
		if(!dfn[ed[i]]){
			tarjan(ed[i]);
			low[now]=min(low[now],low[ed[i]]);
		}else if(!sccnum[ed[i]]){
			low[now]=min(low[now],dfn[ed[i]]);
		}
	}
	if(dfn[now]==low[now]){
		scccnt++;
		do{
			sccnum[s[--stop]]=scccnt;
			mny[scccnt]+=money[s[stop]];
		}while(s[stop]!=now);
	}
}//tarjan的板子
void SPFA(int S){
	queue<int>q;
	r[S]=mny[S];
	q.push(S);
	while(q.size()){
		int kkksc03=q.front();q.pop();
		u[kkksc03]=false;
		for(auto i:v[kkksc03]){
			if(r[kkksc03]+i.second>r[i.first]){
				r[i.first]=r[kkksc03]+i.second;
				if(!u[i.first]){
					u[i.first]=true;
					q.push(i.first);
				}
			}
		}
	}
}//SPFA的板子
int main(){
	read(n);read(m);
	for(int i=1;i<=m;i++){
		int x,y;read(x);read(y);
		add(x,y);
	}
	for(int i=1;i<=n;i++)read(money[i]);
	for(int i=1;i<=n;i++)
		if(!dfn[i])tarjan(i);
	for(int i=1;i<=n;i++)
		for(int j=he[i];j;j=ne[j])
			if(sccnum[i]!=sccnum[ed[j]])
				v[sccnum[i]].push_back(make_pair(sccnum[ed[j]],mny[sccnum[ed[j]]]));//建图
	read(S);SPFA(sccnum[S]);//跑SPFA
	read(P);
	for(int i=1;i<=P;i++){
		read(x);
		ans=max(ans,r[sccnum[x]]);//找最长路
	}
	printf("%d\n",ans);//输出
	return 0;
}
posted @ 2020-10-17 10:16  zhaohaikun  阅读(116)  评论(1编辑  收藏  举报