[BZOJ1179][APIO2009][强连通分量Tarjan+spfa]ATM

[BZOJ1179][APIO2009]ATM

Input

第一行包含两个整数N、M。N表示路口的个数,M表示道路条数。接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道路的起点和终点的路口编号。接下来N行,每行一个整数,按顺序表示每个路口处的ATM机中的钱数。接下来一行包含两个整数S、P,S表示市中心的编号,也就是出发的路口。P表示酒吧数目。接下来的一行中有P个整数,表示P个有酒吧的路口的编号

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

50%的输入保证N, M<=3000。所有的输入保证N, M<=500000。每个ATM机中可取的钱数为一个非负整数且不超过4000。输入数据保证你可以从市中心沿着Siruseri的单向的道路到达其中的至少一个酒吧。

题目大意:在一个各个点都有权值有向图中,从一个点出发,走过的点权值清零。要求问如何走所得的总权值最大,并且可以在酒吧点结束。

题目分析:如果若干个点处在同一个强连通分量中,那么从这个强连通分量中任意一个节点出发必定能到达这个强连通分量中的任意一个点。所以可以把这些点用Tarjan缩成一个点,把强连通分量中的所有的点权加到那个缩成的点,然后把这些缩成的点重构图跑一遍spfa,输出到所有酒吧点的路中最长的即可。

 

下面贴AC代码:

 1 #include <cstdio>
 2 #include <algorithm>
 3 using namespace std;
 4 int n,m,cnt,re_cnt,S,P,CN,maxn,top,dfs_num;
 5 int pre[1000010],re_pre[1000010],bar[1000010],DFN[1000010],LOW[1000010],dye[1000010],size[1000010],tow[1000010],vis[1000010],w[1000010],dist[1000010];
 6 struct pack{int to,next,d;} E[1000010],re_E[1000010];
 7 void add_edge(int x,int y){
 8     E[++cnt].to=y;
 9     E[cnt].next=pre[x];
10     pre[x]=cnt;
11 }
12 void rebuild(){
13     for(int i=1;i<=n;++i)
14         for(int j=pre[i];j;j=E[j].next)
15             if(dye[i]!=dye[E[j].to]&&dye[i]&&dye[E[j].to]){
16                 re_E[++re_cnt].to=dye[E[j].to];
17                 re_E[re_cnt].next=re_pre[dye[i]];
18                 re_E[re_cnt].d=size[dye[E[j].to]];
19                 re_pre[dye[i]]=re_cnt;
20             }
21 }
22 void tarjan(int pos){
23     vis[tow[++top]=pos]=1;
24     DFN[pos]=LOW[pos]=++dfs_num;
25     for(int i=pre[pos];i;i=E[i].next){
26         if(!DFN[E[i].to]){
27             tarjan(E[i].to);
28             LOW[pos]=min(LOW[pos],LOW[E[i].to]);
29         }
30         else if(vis[E[i].to])
31             LOW[pos]=min(LOW[pos],DFN[E[i].to]);
32     }
33     if(DFN[pos]==LOW[pos]){
34         vis[pos]=0;
35         size[dye[pos]=++CN]+=w[pos];
36         while(pos!=tow[top]){
37             size[dye[tow[top]]=CN]+=w[tow[top]];
38             vis[tow[top--]]=0;
39         }
40         --top;
41     }
42 }
43 int spfa(int pos){
44     vis[pos]=1;
45     for(int i=re_pre[pos];i;i=re_E[i].next){
46         int v=re_E[i].to; int k=re_E[i].d;
47         if(dist[pos]+k>dist[v]){
48             dist[v]=dist[pos]+k;
49             if(!vis[v]){
50                 if(spfa(re_E[i].to)) return 1;
51             }
52             else return 1;
53         }
54     }
55     vis[pos]=0;
56     return 0;
57 }
58 int main(){
59     scanf("%d%d",&n,&m);
60     if(!n||!m) {printf("0");return 0;}
61     for(int i=1;i<=m;++i){
62         int a,b;
63         scanf("%d%d",&a,&b);
64         add_edge(a,b);
65     }
66     for(int i=1;i<=n;++i)
67         scanf("%d",&w[i]);
68     scanf("%d%d",&S,&P);
69     for(int i=1;i<=P;++i){
70         int t;
71         scanf("%d",&t);
72         bar[t]=1;
73     }
74     for(int i=1;i<=n;++i)
75         if(!dye[i])
76             tarjan(i);
77     rebuild();
78     dist[dye[S]]=size[dye[S]];
79     spfa(dye[S]);
80     for(int i=1;i<=n;++i)
81         if(bar[i])
82             if(dist[dye[i]]>maxn)
83                 maxn=dist[dye[i]];
84     printf("%d",maxn);
85     return 0;
86 }
时间: 2024-10-12 20:01:42

[BZOJ1179][APIO2009][强连通分量Tarjan+spfa]ATM的相关文章

强连通分量(tarjan求强连通分量)

双DFS方法就是正dfs扫一遍,然后将边反向dfs扫一遍.<挑战程序设计>上有说明. 双dfs代码: 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <vector> 5 6 using namespace std; 7 const int MAXN = 1e4 + 5; 8 vector <int> G[MAXN]; //图的邻接表

ZOJ 3795 Grouping 强连通分量-tarjan

一开始我还天真的一遍DFS求出最长链以为就可以了 不过发现存在有向环,即强连通分量SCC,有向环里的每个点都是可比的,都要分别给个集合才行,最后应该把这些强连通分量缩成一个点,最后保证图里是 有向无环图才行,这个时候再找最长链,当然缩点之后的scc是有权值的,不能只看成1,缩点完了之后,用记忆化搜索DP就可以再On的复杂度内求出结果 所以现学了一下SCC-Tarjan,所谓Scc-tarjan,就是找到强连通分量并且缩点,特别好用,其原理就是利用dfs时间戳,每个点有自己的时间戳,同时再开一个记

HDU 1269 强连通分量tarjan算法

迷宫城堡 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 6655    Accepted Submission(s): 2973 Problem Description 为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N<=10000)和M条通道(M<=100000),每个通道都是单向的,就是说若称某通道连通了A房

图之强连通、强连通图、强连通分量 Tarjan算法

强连通分量 简介 在阅读下列内容之前,请务必了解图论基础部分. 强连通的定义是:有向图 G 强连通是指,G 中任意两个结点连通. 强连通分量(Strongly Connected Components,SCC)的定义是:极大的强连通子图. 这里想要介绍的是如何来求强连通分量. Tarjan 算法 Robert E. Tarjan (1948~) 美国人. Tarjan 发明了很多算法结构.光 Tarjan 算法就有很多,比如求各种联通分量的 Tarjan 算法,求 LCA(Lowest Comm

强连通分量tarjan

#include<iostream>//强连通分量tarjan #include<cstdio> #include<algorithm> #include<queue> #include<cstring> using namespace std; #define maxn 400010 #define INF 1e18 int low[maxn],dfn[maxn],sta[maxn],d[maxn]/*value*/,dis[maxn]/*to

【强连通分量&#183;Tarjan】bzoj1179: [Apio2009]Atm

新博的第一发! 因为这几天切了几道强连通分量,所以从这里begin [题目描述] Siruseri 城中的道路都是单向的.不同的道路由路口连接.按照法律的规定,在每个路口都设立了一个Siruseri 银行的ATM 取款机.令人奇怪的是,Siruseri的酒吧也都设在路口,虽然并不是每个路口都设有酒吧.Banditji 计划实施Siruseri 有史以来最惊天动地的ATM 抢劫.他将从市中心出发,沿着单向道路行驶,抢劫所有他途径的ATM 机,最终他将在一个酒吧庆祝他的胜利.使用高超的黑客技术,他获

求图的强连通分量--tarjan算法

一:tarjan算法详解 ?思想: ? ?做一遍DFS,用dfn[i]表示编号为i的节点在DFS过程中的访问序号(也可以叫做开始时间)用low[i]表示i节点DFS过程中i的下方节点所能到达的开始时间最早的节点的开始时间.(也就是之后的深搜所能到达的最小开始时间)初始时dfn[i]=low[i] ? ?在DFS过程中会形成一搜索树.在搜索树上越先遍历到的节点,显然dfn的值就越小. ? ?DFS过程中,碰到哪个节点,就将哪个节点入栈.栈中节点只有在其所属的强连通分量已经全部求出时,才会出栈. ?

强连通分量tarjan缩点——POJ2186 Popular Cows

这里的Tarjan是基于DFS,用于求有向图的强联通分量. 运用了一个点dfn时间戳和low的关系巧妙地判断出一个强联通分量,从而实现一次DFS即可求出所有的强联通分量. §有向图中, u可达v不一定意味着v可达u.    相互可达则属于同一个强连通分量    (Strongly Connected Component, SCC) §有向图和它的转置的强连通分量相同 §所有SCC构成一个DAG(有向无环图) dfn[u]为节点u搜索的次序编号(时间戳),即首次访问u的时间 low[u]为u或u的

强连通分量tarjan模板复习

对于一个有向图定点的子集,在该子集中任取两点u与v,都能找到一条从u到v的路径,则称该子集是强连通的.若该集合加入到任意点集中,它都不再强连通,则称这个子集是原图的一个强连通分量.任意一张图都可以分解成若干个不相交的强连通分量.这是强连通分量分解.把分解后的强连通分量缩成一个顶点,就可以得到一个有向无环图. 如图: 求一张图的强连通分量的个数,常用tarjan算法,它是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树.搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈