題解 P3627 [APIO2009]搶掠計劃

Siruseri 城中的道路都是單向的。不同的道路由路口連接。按照法律的規定,在每個路口都設立了一個 Siruseri 銀行的 ATM 取款機。令人奇怪的是,Siruseri 的酒吧也都設在路口,雖然並不是每個路口都設有酒吧。

Banditji 計劃實施 Siruseri 有史以來最驚天動地的 ATM 搶劫。他將從市中心出發,沿着單向道路行駛,搶劫所有他途徑的 ATM 機,最終他將在一個酒吧慶祝他的勝利。

使用高超的黑客技術,他獲知了每個 ATM 機中可以掠取的現金數額。他希望你幫助他計算從市中心出發最後到達某個酒吧時最多能搶劫的現金總數。他可以經過同一路口或道路任意多次。但只要他搶劫過某個 ATM 機後,該 ATM 機裏面就不會再有錢了。 例如,假設該城中有 66 個路口,道路的連接情況如下圖所示:

市中心在路口 11,由一個入口符號 → 來標識,那些有酒吧的路口用雙圈來表示。每個 ATM 機中可取的錢數標在了路口的上方。在這個例子中,Banditji 能搶劫的現金總數爲 4747,實施的搶劫路線是:12412351-2-4-1-2-3-5

前置知識:

首先自然就是縮點,圖變成了 DAGDAG。我們之後就可以開始跑最長路了。

講講縮點完的建圖:

以樣例爲例

在這裏插入圖片描述

當然起點也需要權值。

代碼:

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章