题目:
题意:
给定一张没有自环和重边的无向图,每个点有一个权值,求从一个起点出发的最大权值和,不能连续走同一条边,且每个点的权值只会被算一次
分析:
[in a row],这个短语居然有连续的意思,容易导致读错题
容易想到,如果一条链上有环,那么就可以原路返回,否则就不能再走了;先用 tanjar 缩环后变成一棵树,预处理出每个根节点向下走又能返回到根节点的权值和;最后类似树形dp的方法再走一遍,每走一个分支,就把其它分支能回到根的权值和加上即可
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2e5+25;
int n,m,u,v,cnt,cnt1,head1[maxn],head[maxn],per[maxn];
struct edge{
int to,nxt;
}e[maxn<<1],e1[maxn<<1];
inline void add(int u,int v){
e[++cnt] = (edge){v,head[u]};
head[u] = cnt;
}
inline void add1(int u,int v){
e1[++cnt1] = (edge){v,head1[u]};
head1[u] = cnt1;
}
LL w[maxn],dp[maxn];
int dfn[maxn],low[maxn],num,instack[maxn],vis[maxn];
stack<int> s;
void tanjar(int x,int fa){
dfn[x] = low[x] = ++num;
s.push(x); instack[x] = 1;
for(int i = head[x];i > 0;i = e[i].nxt){
int v = e[i].to; if(v == fa) continue;
if(!dfn[v]){
tanjar(v,x);
low[x] = min(low[x],low[v]);
}
else if(instack[v]) low[x] = min(low[x],dfn[v]);
}
if(dfn[x] == low[x]){
int v;
do{
v = s.top(); s.pop();
instack[v] = 0; per[v] = x;
if(v != x) vis[x] = 1,w[x] += w[v]; //将环缩到x点,vis[x]标记x为根的子树下是否存在环
for(int i = head[v]; i > 0;i = e[i].nxt){
int vv = e[i].to;
if(per[vv] == vv){
add1(x,vv); //建立树边
dp[x] += dp[vv];
if(vis[vv]) vis[x] = 1;
}
}
}while(v!=x);
if(vis[x]) dp[x] += w[x]; //子树下有环,返回时自然可以加上x点的权值
}
}
LL ans;
void dfs(int x,LL sum){
sum += dp[x]; if(!vis[x]) sum += w[x];
ans = max(ans,sum);
for(int i = head1[x];i > 0;i=e1[i].nxt){
int v = e1[i].to;
dfs(v,sum-dp[v]);
}
}
int main(){
scanf("%d %d",&n,&m);
for(int i = 1;i<= n; ++i) scanf("%I64d",w+i);
while(m--){
scanf("%d %d",&u,&v);
add(u,v); add(v,u);
}
int ss; scanf("%d",&ss);
tanjar(ss,0); dfs(ss,0);
cout << ans << '\n';
return 0;
}