1. 程式人生 > >【APIO 2009】搶掠計劃

【APIO 2009】搶掠計劃

【題目】

傳送門

題目描述:

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

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

使用高超的黑客技術,他獲知了每個 ATM 機中可以掠取的現金數額。他希望你幫助他計算從市中心出發最後到達某個酒吧時最多能搶劫的現金總數。他可以經過同一路口或道路任意多次。但只要他搶劫過某個 ATM

機後,該 ATM 機裡面就不會再有錢了。

例如,假設該城中有 6 個路口,道路的連線情況如下圖所示:

在這裡插入圖片描述

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

輸入格式:

第一行包含兩個整數 NMN 表示路口的個數,M 表示道路條數。接下來 M 行,每行兩個整數,這兩個整數都在 1N 之間,第 i+1 行的兩個整數表示第 i 條道路的起點和終點的路口編號。

接下來 N 行,每行一個整數,按順序表示每個路口處的 ATM

機中的錢數。接下來一行包含兩個整數 SPS 表示市中心的編號,也就是出發的路口。P 表示酒吧數目。接下來的一行中有 P 個整數,表示 P 個有酒吧的路口的。

輸出格式:

輸出一個整數,表示 Banditji 從市中心開始到某個酒吧結束所能搶劫的最多的現金總數。

樣例資料:

輸入
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

輸出
47

備註:

【資料範圍】
50% 的輸入保證 N, M ≤ 3000
100% 的輸入保證 N, M ≤ 500000
每個 ATM 機中可取的錢數為一個非負整數且不超過 4000
輸入資料保證你可以從市中心沿著 Siruseri

的單向的道路到達其中的至少一個酒吧。

【分析】

題解:Tarjan 縮點+ spfa最長路

先對這個圖縮點,點權為該聯通塊的所有權值和,且只要一個點有酒吧那麼該點也就有酒吧

因為當我們進入該連通塊任意一個點時,就可以直接把整個聯通塊內的所有點都搶完,並且只要該聯通塊有一個有酒吧,那就可以選擇在那裡慶祝

縮完點後,就可以用 spfa 求最長路(用點權來做)。由於已經沒有環了,所以可以直接用

【程式碼】

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 500005
using namespace std;
int n,m,t,sign,sum,tot,top;
int head[N],vv[N],nxt[N],first[N],v[N],next[N];
int d[N],id[N],dfn[N],low[N],sta[N],val[N],money[N];
bool insta[N],a[N],bar[N],vis[N];
void add(int x,int y)
{
	t++;
	next[t]=first[x];
	first[x]=t;
	v[t]=y;
}
void edge(int x,int y)
{
	tot++;
	nxt[tot]=head[x];
	head[x]=tot;
	vv[tot]=y;
}
void Tarjan(int x)
{
	int i,k;
	dfn[x]=low[x]=++sign;
	sta[++top]=x,insta[x]=true;
	for(i=first[x];i;i=next[i])
	{
		k=v[i];
		if(!dfn[k])
		{
			Tarjan(k);
			low[x]=min(low[x],low[k]);
		}
		else  if(insta[k])
		  low[x]=min(low[x],dfn[k]);
	}
	if(low[x]==dfn[x])
	{
		sum++;
		do
		{
			i=sta[top--];
			id[i]=sum;
			insta[i]=false;
			money[sum]+=val[i];
			if(a[i])  bar[sum]=true;
		}while(i!=x);
	}
}
int spfa(int s)
{
	int x,y,i,j,Max;
	d[id[s]]=money[id[s]];Max=d[id[s]];
	queue<int>q;q.push(id[s]);
	while(!q.empty())
	{
		x=q.front();q.pop();
		vis[x]=false;
		for(i=head[x];i;i=nxt[i])
		{
			y=vv[i];
			if(d[y]<d[x]+money[y])
			{
				d[y]=d[x]+money[y];
				if(bar[y])  Max=max(Max,d[y]);
				if(!vis[y])
				{
					q.push(y);
					vis[y]=true;
				}
			}
		}
	}
	return Max;
}
int main()
{
	int x,y,i,j,s,p;
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;++i)
	{
		scanf("%d%d",&x,&y);
		add(x,y);
	}
	for(i=1;i<=n;++i)
	  scanf("%d",&val[i]);
	scanf("%d%d",&s,&p);
	for(i=1;i<=p;++i)
	{
		scanf("%d",&x);
		a[x]=true;
	}
	for(i=1;i<=n;++i)
	  if(!dfn[i])
	    Tarjan(i);
	for(i=1;i<=n;++i)
	  for(j=first[i];j;j=next[j])
	    if(id[i]!=id[v[j]])
	      edge(id[i],id[v[j]]);
	int Max=spfa(s);
	printf("%d",Max);
	return 0;
}