1018 - 強聯通Tarjan - 搶掠計劃(luogu 3627)
阿新 • • 發佈:2018-11-10
分析
由於題目中說了,我們可以重複走一些路口,所以對於一個聯通的部分,我們將其縮為一個點,所有可取的錢數作為它的點權
這樣子,dfs一遍找到最大的值
當然不止我口胡的那麼簡單,還有一些細節要注意
然而你也不用注意這些細節,因為這樣會TLE
(寫了一發,T了三個點)
這樣做主要耗時還是在dfs求最大值上,那我們就想辦法換一種思路
將點權移做邊權,這樣用SPFA跑一個最長路,其效果也是一樣的,但複雜度優秀得多
程式碼
#include<bits/stdc++.h> #define N 500000 #define in read() using namespace std; inline int read(){ char ch;int f=1,res=0; while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1; while(ch>='0'&&ch<='9'){ res=(res<<3)+(res<<1)+ch-'0'; ch=getchar(); } return f==1?res:-res; } int n,m,p,S,val[N],bar[N]; int nxt[N*2],to[N*2],head[N],ecnt=0; void add(int x,int y){ nxt[++ecnt]=head[x];head[x]=ecnt;to[ecnt]=y; } int Nxt[N*2],To[N*2],Head[N],Ecnt=0,W[N*2]; void add2(int x,int y,int z){ Nxt[++Ecnt]=Head[x];Head[x]=Ecnt;To[Ecnt]=y;W[Ecnt]=z; } int dfn[N],low[N],cnt=0,is[N]; stack<int > s; bool insta[N]; int num=0,be[N],sze[N]; void tarjan(int u){ s.push(u);insta[u]=1; dfn[u]=low[u]=++cnt; for(int e=head[u];e;e=nxt[e]){ int v=to[e]; if(!dfn[v]){ tarjan(v); low[u]=min(low[u],low[v]); } else if(insta[v]) low[u]=min(low[u],dfn[v]); } int x; if(dfn[u]==low[u]){ ++num; do{ x=s.top();s.pop(); insta[x]=0;is[num]=is[num]|bar[x]; be[x]=num;sze[num]+=val[x]; }while(x!=u); } } int dis[N],vis[N]; void spfa(int sv){ dis[sv]=0; queue<int> Q; Q.push(sv);vis[sv]=1; while(!Q.empty()){ int u=Q.front();Q.pop();vis[u]=0; for(int e=Head[u];e;e=Nxt[e]){ int v=To[e]; if(dis[v]<dis[u]+W[e]){ dis[v]=dis[u]+W[e]; if(!vis[v]){ Q.push(v); vis[v]=1; } } } } } int main(){ n=in;m=in; int i,j,k; for(i=1;i<=m;++i){ int u=in,v=in; add(u,v); } for(i=1;i<=n;++i) val[i]=in; S=in;p=in; for(i=1;i<=p;++i){ j=in; bar[j]=1; } for(i=1;i<=n;++i) if(!dfn[i]) tarjan(i); for(i=1;i<=n;++i) { for(int e=head[i];e;e=nxt[e]){ j=to[e]; if(be[i]!=be[j]) add2(be[i],be[j],sze[be[j]]); } } add2(num+1,be[S],sze[be[S]]); spfa(num+1); int maxn=-1; for(i=1;i<=num;++i) if(is[i]) maxn=max(maxn,dis[i]); cout<<maxn; return 0; }