换根dp,CF 633F - The Chocolate Spree

一、题目

1、题目描述

2、输入输出

2.1输入
2.2输出

3、原题链接

633F - The Chocolate Spree


二、解题报告

1、思路分析

2600的题,但是不算很困难。

先考虑暴力做法,如何得到两条不相交的路径?

枚举删除的边,得到两棵子树,分别在两棵子树上进行dfs找最长路径

时间复杂度O(N^2)

考虑优化:换根dp

为什么能想到换根dp?

考虑答案可能"长什么样子":

1、两条路径分别在两个不相交子树中,此时,我们利用类似树的直径的求法,一次dfs就能搞定

答案就是两个 /\

2、两条路径在两棵相交子树中

这个时候答案长这个样子:

显然是由三段拼接而来的

对于根节点u,我们考虑固定子树v内的最长一段,然后考虑另外两段怎么选:

子树v1内的一段

子树v2内的一段

u向上的某段

从这三段中选两段

显然要维护根节点子树的最大值、次大值、次次大值

这个我们可以靠换根dp来进行维护

2、复杂度

时间复杂度: O(N)空间复杂度:O(N)

3、代码详解

复制代码
 ​
cpp 复制代码
#include <bits/stdc++.h>
using i64 = long long;
using i128 = __int128;
using PII = std::pair<int, int>;
const int inf = 1e9 + 7, P = 998244353;

struct Node {
    i64 fi, se, th, fiv, sev;  
};

void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    std::vector<std::vector<int>> g(n);
    for (int i = 0; i < n; i ++ ) std::cin >> a[i];
    for (int i = 1, u, v; i < n; i ++ ) {
        std::cin >> u >> v;
        -- u, -- v;
        g[u].push_back(v), g[v].push_back(u);
    }
    
    std::vector<i64> subAns(n);
    std::vector<Node> nodes(n);

    i64 res = 0;
    auto dfs1 = [&](auto&& self, int u, int fa) -> i64 {
        subAns[u] = a[u];
        i64 maxS = a[u], maxSubAnsV = 0;
        Node& p = nodes[u];
        for (int v : g[u]) {
            if (v == fa) continue;
            i64 s = self(self, v, u);
            res = std::max(res, maxSubAnsV + subAns[v]);
            maxSubAnsV = std::max(maxSubAnsV, subAns[v]);
            subAns[u] = std::max(subAns[u], maxS + s);
            maxS = std::max(maxS, s + a[u]);
            if (s > p.fi) {
                p.th = p.se, p.se = p.fi, p.fi = s;
                p.sev = p.fiv, p.fiv = v;
            }
            else if (s > p.se) {
                p.th = p.se, p.se = s;
                p.sev = v;
            }
            else if(s > p.th)
                p.th = s;
        }
        subAns[u] = std::max(subAns[u], maxSubAnsV);
        return maxS;
    };

    dfs1(dfs1, 0, -1);

    auto dfs2 = [&](auto&& self, int u, int fa, i64 maFa) -> void {
        Node& p = nodes[u];
        for (int v : g[u]) {
            if (v == fa) continue;
            if (v == p.fiv) {
                res = std::max(res, subAns[v] + a[u] + p.se + std::max(p.th, maFa));
                self(self, v, u, a[u] + std::max(p.se, maFa));
            }
            else {
                i64 s = p.se;
                if (v == p.sev) s = p.th;
                res = std::max(res, subAns[v] + a[u] + p.fi + std::max(s, maFa));
                self(self, v, u, a[u] + std::max(p.fi, maFa));
            }
        }
    };
    dfs2(dfs2, 0, -1, 0);
    std::cout << res;
}

int main(int argc, char** argv) {
    std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
    int _ = 1;
    // std::cin >> _;
    while (_ --)
        solve();
    return 0;
}
相关推荐
wuweijianlove4 小时前
算法性能的渐近与非渐近行为对比的技术4
算法
_dindong4 小时前
cf1091div2 C.Grid Covering(数论)
c++·算法
AI成长日志4 小时前
【Agentic RL】1.1 什么是Agentic RL:从传统RL到智能体学习
人工智能·学习·算法
黎阳之光5 小时前
黎阳之光:视频孪生领跑者,铸就中国数字科技全球竞争力
大数据·人工智能·算法·安全·数字孪生
skywalker_115 小时前
力扣hot100-3(最长连续序列),4(移动零)
数据结构·算法·leetcode
6Hzlia5 小时前
【Hot 100 刷题计划】 LeetCode 17. 电话号码的字母组合 | C++ 回溯算法经典模板
c++·算法·leetcode
wfbcg5 小时前
每日算法练习:LeetCode 209. 长度最小的子数组 ✅
算法·leetcode·职场和发展
_日拱一卒5 小时前
LeetCode:除了自身以外数组的乘积
数据结构·算法·leetcode
计算机安禾5 小时前
【数据结构与算法】第36篇:排序大总结:稳定性、时间复杂度与适用场景
c语言·数据结构·c++·算法·链表·线性回归·visual studio
SatVision炼金士5 小时前
合成孔径雷达干涉测量(InSAR)沉降监测算法体系
算法