【CodeForces - 1220E】:Tourism【tanjar缩环+树形dp】

题目:

CodeForces - 1220E:Tourism

题意:

给定一张没有自环和重边的无向图,每个点有一个权值,求从一个起点出发的最大权值和,不能连续走同一条边,且每个点的权值只会被算一次

分析:

[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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值