题意
在一个无向图上有 n n n 个城市,每个城市都有一个价值 w i w_i wi,现从起点 s s s 出发,过程中不能连续两次经过同一条边,问能够得到的最大价值。
由于不能连续两次经过同一条边,因此一定会贪心的选择所有带有环的路,最后再选择一条贡献最大但没有环的路走到底。
采用 T a r j a n Tarjan Tarjan 缩点转化为有向无环图之后,与起点 s s s 在同一连通块的可以看作是以 s s s 为根的有向树(返祖边可以在 d f s dfs dfs 中特判掉)。在 T a r j a n Tarjan Tarjan 中处理每个强连通分块是单点还是环,并以 y e s yes yes 数组标记,则在树中若某一结点的后代存在 y e s = 1 yes=1 yes=1,则可以经过该结点到最底端的 y e s = 1 yes=1 yes=1 的结点并通过绕环再回来,显然这整段的贡献值都能够被纳入答案。
由于最后会选择贡献最大但没有环的路走到底(如果存在的话),因此对于某个儿子是 y e s = 0 yes=0 yes=0 的结点,都需要更新无环路能够取到的贡献最大值。但对于当前结点而言,只有其所有儿子都是 y e s = 0 yes=0 yes=0,才能够更新这条无环路的贡献,否则当前结点的贡献会被重复计算(如果有儿子的 y e s = 1 yes=1 yes=1,该结点一定会被纳入答案内,若更新了无环路,可能会多计算一次)。
#include <bits/stdc++.h>
#define int long long
#define PII pair<int,int>
using namespace std;
const int N=2e5+10;
vector <int> h[N],H[N];
bool vis[N],in[N],yes[N];
int dfn[N],low[N],suo[N],num[N],value[N],total[N],fail[N],dp[N];
stack<int> st;
vector<PII> q;
int tmp=0,cnt=0,x=0,sum=0,maxx=0;
int ans=0;
void tarjan(int u,int fa){
dfn[u]=low[u]=++x;
in[u]=true;
st.push(u);
for(auto v:h[u]){
if(v==fa)
continue;
if(!dfn[v]){
tarjan(v,u);
low[u]=min(low[u],low[v]);
}
else if(in[v])
low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
cnt++;
do{
tmp=st.top();
//cout<<tmp<<" "<<value[tmp]<<endl;
st.pop();
in[tmp]=false;
suo[tmp]=cnt;
total[cnt]+=value[tmp];
if(u!=tmp)
yes[cnt]=1;//该强连通分量内有环
}
while(u!=tmp);
}
dp[cnt]=total[cnt];
}
void dfs(int u){
vis[u]=1;
for(auto i:H[u]){
if(vis[i])
continue;
dfs(i);
if(yes[i]){
yes[u]=1;
dp[u]+=dp[i];
}
else{
fail[u]=max(fail[u],fail[i]);
}
}
if(!yes[u]){
fail[u]+=total[u];
}
maxx=max(maxx,fail[u]);
}
signed main(){
int n,m,s,u,v;
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>value[i];
for(int i=1;i<=m;i++){
cin>>u>>v;
q.push_back({u,v});
q.push_back({v,u});
h[u].push_back(v);
h[v].push_back(u);
}
cin>>s;
for(int i=1;i<=n;i++){
if(!dfn[i])
tarjan(i,0);
}
// for(int i=1;i<=n;i++)
// cout<<suo[i]<<" ";
// cout<<endl;
// for(int i=1;i<=cnt;i++)
// cout<<total[i]<<" ";
// cout<<endl;
for(auto i:q){//建新图
u=suo[i.first],v=suo[i.second];
if(u==v)
continue;
H[u].push_back(v);
}
dfs(suo[s]);//预处理每条路径是否可返回
if(yes[suo[s]])
cout<<dp[suo[s]]+maxx;
else
cout<<max(dp[suo[s]],maxx);
return 0;
}