BZOJ1179: [Apio2009]Atm
1179: [Apio2009]Atm
Time Limit: 15 Sec Memory Limit: 162 MBSubmit: 1427 Solved: 544
[Submit][Status]
Description
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
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
1 5
1 4
4
3
5
6
Sample Output
47
HINT
50%的输入保证N, M<=3000。所有的输入保证N, M<=500000。每个ATM机中可取的钱数为一个非负整数且不超过4000。输入数据保证你可以从市中心沿着Siruseri的单向的道路到达其中的至少一个酒吧。
Source
题解:
tarjan之后以scc[s]为起点做一遍spfa即可,若该scc内有酒吧则用它的d值更新答案
重构图的一种比较好的方法就是把所有的边记下来,tarjan之后可以memset(head,0,sizeof(head)),tot=0,重新insert
就不用在开其他的数组了,具体看代码
代码:
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 #include<iostream> 7 #include<vector> 8 #include<map> 9 #include<set> 10 #include<queue> 11 #define inf 1000000000 12 #define maxn 500000+10000 13 #define maxm 500+100 14 #define eps 1e-10 15 #define ll long long 16 using namespace std; 17 inline int read() 18 { 19 int x=0,f=1;char ch=getchar(); 20 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 21 while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();} 22 return x*f; 23 } 24 int n,m,ss,p,cnt,tot,ti,top,head[maxn],low[maxn],dfn[maxn],scc[maxn]; 25 int sta[maxn],from[maxn],go[maxn],q[2*maxn],a[maxn]; 26 ll ans,s[maxn],d[maxn]; 27 bool b[maxn],c[maxn],v[maxn]; 28 29 struct edge{int go,next;}e[maxn]; 30 void insert(int x,int y) 31 { 32 e[++tot].go=y;e[tot].next=head[x];head[x]=tot; 33 } 34 void dfs(int x) 35 { 36 int y; 37 low[x]=dfn[x]=++ti;sta[++top]=x; 38 for(int i=head[x];i;i=e[i].next) 39 if (!dfn[y=e[i].go]) 40 { 41 dfs(y); 42 low[x]=min(low[x],low[y]); 43 } 44 else if(!scc[y])low[x]=min(low[x],dfn[y]); 45 if(low[x]==dfn[x]) 46 { 47 cnt++; 48 while(1) 49 { 50 scc[y=sta[top--]]=cnt;s[cnt]+=a[y]; 51 if(b[y])c[cnt]=1; 52 if(y==x)break; 53 } 54 } 55 } 56 void tarjan() 57 { 58 for(int i=1;i<=n;i++)if(!dfn[i])dfs(i); 59 } 60 void spfa() 61 { 62 for(int i=1;i<=cnt;++i) d[i]=0; 63 memset(v,0,sizeof(v)); 64 int l=0,r=1,x,y;q[1]=scc[ss];d[scc[ss]]=s[scc[ss]]; 65 while(l!=r) 66 { 67 x=q[++l];if(l==maxn)l=0;v[x]=0; 68 for(int i=head[x];i;i=e[i].next) 69 if(d[x]+s[y=e[i].go]>d[y]) 70 { 71 d[y]=d[x]+s[y]; 72 if(!v[y]){v[y]=1;q[++r]=y;if(r==maxn)r=0;} 73 } 74 } 75 } 76 int main() 77 { 78 freopen("input.txt","r",stdin); 79 freopen("output.txt","w",stdout); 80 n=read();m=read(); 81 int x,y; 82 for(int i=1;i<=m;i++)from[i]=read(),go[i]=read(),insert(from[i],go[i]); 83 for(int i=1;i<=n;i++)a[i]=read(); 84 ss=read();p=read(); 85 for(int i=1;i<=p;i++)x=read(),b[x]=1; 86 tarjan(); 87 memset(head,0,sizeof(head));tot=0; 88 for(int i=1;i<=m;i++) 89 if(scc[x=from[i]]!=scc[y=go[i]]) 90 { 91 insert(scc[x],scc[y]); 92 } 93 spfa(); 94 ans=0; 95 for(int i=1;i<=cnt;i++)if(c[i])ans=max(ans,d[i]); 96 printf("%lld\n",ans); 97 return 0; 98 }
UPD:为何现在看着这题觉得不应该SPFA,而是应该拓扑排序之后求个最长链什么的,又因为限制了起点,所以可以对事先入队的节点的函数值动手脚就行了23