bzoj 1179: [Apio2009]Atm (Tarjan, + 最长路)

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

HINT

 

Source

 

思路: 

看完题就是个裸题。 

Tarjan 一下。 找到环。

缩点之后,跑最长路就可以了。 

注意在缩点的时候,不要出现自环。 

that's all

 

#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+1000;
int Head[N],Next[N],To[N],Val[N],cnt;
int n,m,S,p;
bool vis[N];
void add(int u, int v){
	Next[++cnt] = Head[u];
	Head[u] = cnt;
	To[cnt] = v;
}
void init(){
	int x,y;
	scanf("%d%d",&n,&m);
	for (int i = 0; i < m; ++i){
		scanf("%d%d",&x,&y);
		add(x,y);
	}
	for (int i = 1; i <= n; ++i)
		scanf("%d",&Val[i]);
	scanf("%d%d",&S,&p);
	for (int i = 0;i < p; ++i)
		scanf("%d",&x),vis[x] = 1;
}
stack<int>q;
int low[N],dfn[N],tim,scc[N],cont;
void Tarjan(int u){
	low[u] = dfn[u] = ++tim;
	q.push(u);
	for (int i = Head[u]; i; i = Next[i]){
		int v = To[i];
		if (!dfn[v]){
			Tarjan(v);
			low[u] = min(low[u],low[v]);
		} else if (!scc[v]) low[u] = min(low[u],dfn[v]);
	}
	if (low[u] == dfn[u]){
		cont++;
		while(1){
			int v = q.top(); q.pop();
			scc[v] = cont; 
			if (v == u) break;
		}
	}
}
vector<int>f[N];
int val[N],dis[N];
bool dep[N];
void spfa(int u){
	queue<int>Q;
	Q.push(u); dis[u] = val[u]; dep[u] = 1;
	while(!Q.empty()){
		int u = Q.front(); Q.pop(); dep[u] = 0;
		int ss = f[u].size();

		for (int i = 0; i < ss; ++i){
			int v = f[u][i];
			if (dis[v] < dis[u] + val[v]){
				dis[v] = dis[u] + val[v];
				if (dep[v] == 0){
					dep[v] = 1;
					Q.push(v);
				}
			}
		}
	}
}
int main(){
	init();
	Tarjan(S);
	int u,v;
	for (int i = 1; i <= n; ++i){
		u = scc[i];
		val[u] += Val[i];
		for (int j = Head[i]; j; j = Next[j]){
			v = scc[To[j]];
			if (v != u) 	f[u].push_back(v);
		}
	}
	spfa(scc[S]);

	int ans = 0;
	for (int i = 1; i <= n;++i)
		if (vis[i]) ans = max(ans,dis[scc[i]]);
	printf("%d\n",ans);

	return 0;
}

/*
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

*/

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值