From d276eea07cc597381273dbe034f92864d0fdb7dc Mon Sep 17 00:00:00 2001 From: Zengtudor Date: Tue, 19 Nov 2024 10:44:23 +0800 Subject: [PATCH] update --- src/20241118/T541156.cpp | 118 +++++++++++++------ src/20241118/T541156.md | 239 +++++++++++++++++++++++++++++++++++---- 2 files changed, 299 insertions(+), 58 deletions(-) diff --git a/src/20241118/T541156.cpp b/src/20241118/T541156.cpp index c4b2902..a86530a 100644 --- a/src/20241118/T541156.cpp +++ b/src/20241118/T541156.cpp @@ -1,46 +1,98 @@ +#include #include #include #include #include #include - + using ll = int64_t; using std::cin, std::cout; - -const ll maxn = 5e5+5; -ll n, k, a[maxn], ans[maxn]; -std::vector> adj[maxn]; -std::map dp[maxn];//dp[i][j]:第i个节点距离颜色j的最大值 - -void dfs(const ll &fth, const ll &u)noexcept{ - dp[u].emplace(a[u],0); - // dp[u][a[u]]=0; - for(const auto& [v,w]:adj[u]){ - if(v==fth)continue; - dfs(u,v); - for(const auto& [kk,vv]:dp[v]){ - if((dp[u][kk]>0||a[u]==kk)&&(dp[v][kk]>0||a[v]==kk)){ - //更新与u,v子树有关的红温值 - ans[kk]=std::max(ans[kk], dp[u][kk] + dp[v][kk] + w); - } - dp[u][kk]=std::max(dp[u][kk],dp[v][kk]+w); - if(a[u]==kk)ans[kk]=std::max(ans[kk],dp[u][kk]);//更新当前节点的红温值 + +// 定义最大节点数为5e5加5,以确保足够的空间 +const ll maxn = 5e5 + 5; +ll n, k; // n: 树的节点数,k: 暴龙的种类数 +ll a[maxn]; // a[i]: 第i个节点的暴龙种类 +ll ans[maxn]; // ans[i]: 第i种暴龙的红温值 +std::vector> adj[maxn]; // adj[u]: 节点u的邻居及边权 +std::map dp[maxn]; // dp[u][j]: 节点u到子树中种类j的最大距离 + +// 优化输入读取,提高IO速度 +struct CinNum { + char c; + ll n, w; + // 重载右移运算符以自定义输入方式 + CinNum &operator>>(ll &num) { + c = n = 0; + w = 1; + // 跳过非数字字符,记录负号 + while (!isdigit(c = getchar())) { + if (c == '-') w = -1; } + // 读取数字 + while (isdigit(c)) { + n = n * 10 + (c - '0'); + c = getchar(); + } + num = n * w; + return *this; + } +} cinn; +// 使用自定义的CinNum来替代标准输入 +#define cin cinn + +/** + * @brief 使用深度优先搜索 (DFS) 计算每种暴龙的红温值 + * + * @param fth 当前节点的父节点 + * @param u 当前处理的节点 + */ +void dfs(const ll &fth, const ll &u) noexcept { + // 初始化当前节点的dp,自己到自己距离为0 + dp[u].emplace(a[u], 0); + + // 遍历所有邻接节点 + for (const auto& [v, w] : adj[u]) { + if (v == fth) continue; // 避免回到父节点 + dfs(u, v); // 递归处理子节点 + + // 遍历子节点v的所有暴龙种类及其对应的最大距离 + for (const auto& [kk, vv] : dp[v]) { + // 如果当前节点u已经有这种暴龙类型,且子节点v也有 + if ((dp[u][kk] > 0 || a[u] == kk) && (dp[v][kk] > 0 || a[v] == kk)) { + // 更新ans[kk]为u和v子树中这种暴龙类型的最大距离 + ans[kk] = std::max(ans[kk], dp[u][kk] + dp[v][kk] + w); + } + // 更新u节点到这种暴龙类型的最大距离 + dp[u][kk] = std::max(dp[u][kk], dp[v][kk] + w); + + // 如果当前节点u本身就是这种暴龙类型,进一步更新ans[kk] + if (a[u] == kk) { + ans[kk] = std::max(ans[kk], dp[u][kk]); + } + } + // 清空子节点v的dp以节省内存,因为已经合并到u的dp中了 dp[v].clear(); } } - + int main(){ - std::iostream::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr); - - cin>>n>>k; - for(ll i{1};i<=n;i++)cin>>a[i]; - for(ll i{1};i>u>>v>>w; - adj[u].emplace_back(v,w); - adj[v].emplace_back(u,w); + // 读取节点数n和暴龙种类数k + cin >> n >> k; + + // 读取每个节点的暴龙种类 + for(ll i = 1; i <= n; i++) cin >> a[i]; + + // 读取树的边信息,并构建邻接表 + for(ll i = 1; i < n; i++){ + ll u, v, w; + cin >> u >> v >> w; + adj[u].emplace_back(v, w); + adj[v].emplace_back(u, w); } - dfs(0,1); - for(ll i{1};i<=k;i++)cout<关于使用ai +> +>1. 下面的代码是本人手写 +>2. 解析由ai生成模型为chatgpt o1-mini + ```cpp -if((a[u] == kk || dp[u][kk] > 0) && (a[v] == kk || dp[v][kk] > 0)) { - ans[kk] = max(ans[kk], dp[u][kk] + dp[v][kk] + w); +#include +#include +#include +#include +#include +#include + +using ll = int64_t; +using std::cin, std::cout; + +// 定义最大节点数为5e5加5,以确保足够的空间 +const ll maxn = 5e5 + 5; +ll n, k; // n: 树的节点数,k: 暴龙的种类数 +ll a[maxn]; // a[i]: 第i个节点的暴龙种类 +ll ans[maxn]; // ans[i]: 第i种暴龙的红温值 +std::vector> adj[maxn]; // adj[u]: 节点u的邻居及边权 +std::map dp[maxn]; // dp[u][j]: 节点u到子树中种类j的最大距离 + +// 优化输入读取,提高IO速度 +struct CinNum { + char c; + ll n, w; + // 重载右移运算符以自定义输入方式 + CinNum &operator>>(ll &num) { + c = n = 0; + w = 1; + // 跳过非数字字符,记录负号 + while (!isdigit(c = getchar())) { + if (c == '-') w = -1; + } + // 读取数字 + while (isdigit(c)) { + n = n * 10 + (c - '0'); + c = getchar(); + } + num = n * w; + return *this; + } +} cinn; +// 使用自定义的CinNum来替代标准输入 +#define cin cinn + +/** + * @brief 使用深度优先搜索 (DFS) 计算每种暴龙的红温值 + * + * @param fth 当前节点的父节点 + * @param u 当前处理的节点 + */ +void dfs(const ll &fth, const ll &u) noexcept { + // 初始化当前节点的dp,自己到自己距离为0 + dp[u].emplace(a[u], 0); + + // 遍历所有邻接节点 + for (const auto& [v, w] : adj[u]) { + if (v == fth) continue; // 避免回到父节点 + dfs(u, v); // 递归处理子节点 + + // 遍历子节点v的所有暴龙种类及其对应的最大距离 + for (const auto& [kk, vv] : dp[v]) { + // 如果当前节点u已经有这种暴龙类型,且子节点v也有 + if ((dp[u][kk] > 0 || a[u] == kk) && (dp[v][kk] > 0 || a[v] == kk)) { + // 更新ans[kk]为u和v子树中这种暴龙类型的最大距离 + ans[kk] = std::max(ans[kk], dp[u][kk] + dp[v][kk] + w); + } + // 更新u节点到这种暴龙类型的最大距离 + dp[u][kk] = std::max(dp[u][kk], dp[v][kk] + w); + + // 如果当前节点u本身就是这种暴龙类型,进一步更新ans[kk] + if (a[u] == kk) { + ans[kk] = std::max(ans[kk], dp[u][kk]); + } + } + // 清空子节点v的dp以节省内存,因为已经合并到u的dp中了 + dp[v].clear(); + } +} + +int main(){ + // 读取节点数n和暴龙种类数k + cin >> n >> k; + + // 读取每个节点的暴龙种类 + for(ll i = 1; i <= n; i++) cin >> a[i]; + + // 读取树的边信息,并构建邻接表 + for(ll i = 1; i < n; i++){ + ll u, v, w; + cin >> u >> v >> w; + adj[u].emplace_back(v, w); + adj[v].emplace_back(u, w); + } + + // 从节点1开始DFS遍历整棵树,假设节点1为根节点,父节点为0 + dfs(0, 1); + + // 输出每种暴龙的红温值 + for(ll i = 1; i <= k; i++) cout << ans[i] << '\n'; } ``` -### 变量说明 +### 代码详解 -- `kk`: 表示当前正在处理的暴龙类型的标识符(通常是种类编号)。 -- `a[u]`: 当前节点 `u` 的暴龙类型。 -- `dp[u][kk]`: 从当前节点 `u` 开始的子树中,该暴龙类型 `kk` 的最大距离。 -- `dp[v][kk]`: 从子节点 `v` 开始的子树中,该暴龙类型 `kk` 的最大距离。 -- `w`: 当前节点 `u` 和子节点 `v` 之间的边的权重(即两者之间的距离)。 -- `ans[kk]`: 用于存储当前暴龙类型 `kk` 的红温值(最大距离)。 +1. **数据结构选择**: + - **邻接表**: 使用`std::vector> adj[maxn];`来存储树的邻接表,每个节点存储与之相连的节点及边权。 + - **动态规划表(DP)**: 使用`std::map dp[maxn];`来存储每个节点到其子树中不同暴龙种类的最大距离。这里使用`std::map`是因为暴龙种类k可能较多且不连续。 -### 逻辑解释 +2. **输入优化**: + - 定义了一个自定义的`CinNum`结构体,通过重载`>>`运算符来快速读取整数。这在处理大数据量时能够显著提高输入速度。 -1. **类型匹配**: - - `(a[u] == kk || dp[u][kk] > 0)`: - - 这个条件检查当前节点 `u` 是否是类型 `kk` 或者在 `u` 的子树中已经存在类型 `kk` 的暴龙。如果存在,可以考虑将 `u` 和 `v` 之间的距离计算入 `ans[kk]`。 +3. **深度优先搜索(DFS)**: + - **初始化**: 对于每个节点u,将自己的暴龙种类`a[u]`加入`dp[u]`,初始距离为0。 + - **递归处理子节点**: 对每个子节点v,递归调用`dfs(u, v)`。 + - **合并子节点DP**: 对于子节点v的每个暴龙种类kk,检查当前节点u是否也有这种暴龙。如果有,则计算通过边(u, v)连接后的距离,并更新`ans[kk]`为更大的值。 + - **更新u节点的DP**: 将子节点v的距离加上边权w,与当前的`dp[u][kk]`比较,取较大者。 + - **清理资源**: 处理完子节点v后,清空`dp[v]`以节省内存。 - - `(a[v] == kk || dp[v][kk] > 0)`: - - 这个条件检查子节点 `v` 是否是类型 `kk` 或者在 `v` 的子树中已经存在类型 `kk` 的暴龙。类似地,如果存在,也可以考虑将 `u` 和 `v` 之间的距离计算入 `ans[kk]`。 - -2. **计算距离**: - - 如果以上两个条件都是满足的,表示 `u` 和 `v` 之间的边可以被计算在类型 `kk` 的红温值中,因此最大距离被更新为: - ```cpp - ans[kk] = max(ans[kk], dp[u][kk] + dp[v][kk] + w); - ``` - - 这里的 `dp[u][kk] + dp[v][kk] + w` 即为通过当前边 `u-v` 连接的所有类型为 `kk` 的暴龙之间的距离。 +4. **输出结果**: + - 遍历每种暴龙种类,输出其对应的红温值。若某种暴龙在树中数量少于2,则红温值保持初始化的0。 ### 总结 -这个 `if` 判断的作用在于决定是否可以将当前边 `u-v` 的权重 `w` 加入到 `ans[kk]` 中,从而更新当前暴龙类型 `kk` 的最大红温值。这确保了只有在当前节点及其子节点之间具有相同暴龙类型时,才会合并和计算它们之间的边的影响。这样能够更精确地得到不同类型暴龙之间的距离关系。 \ No newline at end of file +该算法利用深度优先搜索(DFS)结合动态规划(DP)的思想,高效地计算了树中每种暴龙类型的最大距离(红温值)。具体步骤如下: + +1. **DFS遍历**: 从根节点开始,递归遍历整个树结构。 +2. **动态规划**: 在遍历的过程中,维护每个节点到其子树中特定暴龙类型的最大距离。 +3. **答案更新**: 通过合并子节点的信息,动态地更新每种暴龙类型的最大距离。 +4. **复杂度分析**: + - 对于每个节点,可能需要处理最多k种暴龙类型。因此,时间复杂度在最坏情况下为O(nk)。由于k和n均可达5e5,这种方法在大规模数据下会超过时间限制。 +5. **适用性**: + - 该代码适用于子任务1,规模较小(n ≤ 3000)的情况,能够在有限的时间内完成计算。 + - 对于更大规模的数据,需要优化数据结构和算法,例如使用更高效的方式存储DP信息(如并查集、最低公共祖先LCA等技术),以降低时间和空间复杂度。 + +希望这些解释和注释对您理解和优化代码有所帮助!如果有进一步的问题,欢迎继续交流。 \ No newline at end of file