鱼C论坛

 找回密码
 立即注册
查看: 3491|回复: 23

[心之碎片][0622] - 树网的核

[复制链接]
发表于 2024-6-22 17:09:22 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 柿子饼同学 于 2024-6-22 19:30 编辑
树网的核 link
1 遍过
4 h


知识点
单调队列


思路
直径的性质: 到一个点的最远距离的点在直径上, 偏心距只会在直径两个端点和不在直径上的路径上产生答案
###1
先找出直径, 记下来, 对于每个直径上的点求出这个点不过直径所能走到最远的点的距离, 记作 val
然后在直径上跑单调队列, 对于一条路径 , 它的偏心距就是 max(路径中val的最大值, 直径两端点到路径端点的最大值)
###2
可以简单一些, 只求出 max(一个点不过直径的最远距离)
根据直径的性质可知 , 当路径不包含这个最大值的点时, 路径端点到直径端点的答案更大
当包含时, 可以让路径端点到直径端点和这个取 max 即可
O(n)



需要进步的地方
代码太长, 加了一些不必要的数组, 要精简代码
一开始写的时候没想清楚, 浪费了时间
整体想不出来, 可以想想单个点是怎么算答案的, 对思考有帮助
先想想一个操作影响的范围, 不要一直想着 memset 0
II 方法很好, 求直径之后沿着 f 数组跳上去即可, 不用截下来或打标记
写 II 的时候 dfs 忘记
...


最终代码

  1. #include <bits/stdc++.h>
  2. using namespace std;

  3. const int N = 5e5 + 5;
  4. typedef long long ll;

  5. struct Edge{
  6.     int v, w;
  7. };

  8. int n;
  9. ll s;
  10. vector<Edge> e[N];

  11. // f 父节点, d 到根的距离
  12. int p, q, f[N];
  13. ll d[N];

  14. void dfs1(int u, int pa, int & id){
  15.     for(auto ed : e[u]){
  16.         if(ed.v == pa) continue;
  17.         f[ed.v] = u;
  18.         d[ed.v] = d[u] + ed.w;

  19.         if(d[ed.v] > d[0]){
  20.             d[0] = d[ed.v];
  21.             id = ed.v;
  22.         }

  23.         dfs1(ed.v, u, id);
  24.     }
  25. }

  26. void Work(){
  27.     f[1] = 0, d[1] = 0;
  28.     dfs1(1, 0, p); // 直径一端点是 p
  29.     f[p] = 0, d[p] = 0, d[0] = 0;
  30.     dfs1(p, 0, q); // 直径顺序: p -> q
  31.     // 不需要 memset, 后续计算不会算到直径
  32. }

  33. // 求不过直径的最大距离
  34. ll mxd = 0;

  35. void dfs2(int u, int f){
  36.     for(auto ed : e[u]){
  37.         if(ed.v == f) continue;
  38.         d[ed.v] = d[u] + ed.w;
  39.         if(d[ed.v] > mxd) mxd = d[ed.v];
  40.         dfs2(ed.v, u);
  41.     }
  42. }

  43. int main(){
  44.     ios::sync_with_stdio(0);
  45.     cin.tie(0);

  46.     cin >> n >> s;
  47.     for(int i = 1; i < n; i++){
  48.         int x, y, z;
  49.         cin >> x >> y >> z;
  50.         e[x].push_back({y, z});
  51.         e[y].push_back({x, z});
  52.     }

  53.     Work();
  54.    
  55.     // 求 mxd
  56.     // j 是 i 的上一个节点
  57.     for(int i = q, j = 0; i; j = i, i = f[i]){
  58.         for(auto ed : e[i]){
  59.             if(ed.v == f[i] || ed.v == j) continue;

  60.             // 现在只有不在直径上的点了
  61.             d[ed.v] = ed.w;
  62.             mxd = max(mxd, d[ed.v]);
  63.             dfs2(ed.v, i);
  64.         }
  65.     }

  66.     // 尺取路径 [i, j]
  67.     ll ans = 2e18;
  68.     for(int i = q, j = q; i; i = f[i]){
  69.         while(f[j] != f[i] && d[j] - d[i] > s) j = f[j];
  70.         ans = min(ans, max({mxd, d[i], d[q] - d[j]}));
  71.     }

  72.     cout << ans;

  73.     return 0;
  74. }
复制代码
一开始的代码
  1. /*
  2. 找直径, 标记, 取下直径
  3. 每个点求一个不在直径上的侧边最远点距离
  4. 每个点的权值 max(最远点距, 直径两端点到点的距离)
  5. 单调队列跑直径, 求最大值最小
  6. */

  7. #include <bits/stdc++.h>
  8. using namespace std;

  9. typedef long long ll;

  10. const int N = 5e5 + 5;

  11. struct Edge{
  12.     int v, w;
  13. };

  14. vector<Edge> e[N];
  15. int n;
  16. ll m;

  17. // 找最远点
  18. bitset<N> vis;
  19. int f[N];
  20. ll maxd, id;
  21. ll dist[N];
  22. int d[N], cnt;

  23. void dfs(int u, int pa, ll dis){
  24.     f[u] = pa;
  25.     if(dis > maxd){
  26.         maxd = dis;
  27.         id = u;
  28.     }

  29.     for(auto ed : e[u]){
  30.         if(ed.v == pa || vis[ed.v]) continue;
  31.         dfs(ed.v, u, dis + ed.w);
  32.     }
  33. }

  34. // 求直径, 扒下来
  35. void Getd(){
  36.     dfs(1, 0, 0);
  37.     memset(f, 0, sizeof(f));
  38.     maxd = 0;
  39.     int rt = id;
  40.     dfs(rt, 0, 0);
  41.     maxd = 0;
  42.    
  43.     while(id != rt){
  44.         d[++cnt] = id;
  45.         vis[id] = 1;

  46.         for(auto ed : e[id]){
  47.             if(ed.v == f[id]){
  48.                 dist[cnt] = ed.w;
  49.                 break;
  50.             }
  51.         }
  52.         id = f[id];
  53.     }
  54.     vis[rt] = 1;
  55.     d[++cnt] = rt;
  56.     for(int i = cnt - 1; i >= 1; i--) dist[i] += dist[i + 1];
  57. }

  58. // 求权值
  59. ll val[N];

  60. void Getval(){
  61.     for(int i = 1; i <= cnt; i++){
  62.         dfs(d[i], f[d[i]], 0);
  63.         val[i] = maxd;
  64.         maxd = 0;
  65.     }
  66. }

  67. int q[N], l = 1, r = 0;

  68. int main(){
  69.     ios::sync_with_stdio(0);
  70.     cin.tie(0);

  71.     cin >> n >> m;
  72.     for(int i = 1; i < n; i++){
  73.         int x, y, z;
  74.         cin >> x >> y >> z;
  75.         e[x].push_back({y, z});
  76.         e[y].push_back({x, z});
  77.     }

  78.     Getd();
  79.     Getval();

  80.     int last = 1;
  81.     ll ans = LONG_LONG_MAX;

  82.     for(int i = 1; i <= cnt; i++){
  83.         while(r >= l && val[q[r]] <= val[i]) r--;
  84.         while(last <= i && dist[last] - dist[i] > m) last++;
  85.         q[++r] = i;
  86.         while(l <= r && q[l] < last) l++;

  87.         ans = min(ans, max({dist[1] - dist[last], dist[i], val[q[l]]}));
  88.     }

  89.     cout << ans;

  90.     return 0;
  91. }
复制代码


评分

参与人数 1荣誉 +5 鱼币 +5 贡献 +3 收起 理由
小肥狼haoran + 5 + 5 + 3 牛皮先支持一波

查看全部评分

本帖被以下淘专辑推荐:

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2024-6-22 17:10:41 | 显示全部楼层
嘿!看起来你在处理一个关于树结构(特别是树的直径和核)的编程问题。让我帮你解读一下这个有点复杂的思路,顺便加点幽默感来让它更容易理解。

### 问题背景:
我们有一个树形结构,树的节点数为 `n`。目标是找出树的“核”,即使得某种距离最小化的特殊点。

### 核心概念解释:
1. **树的直径**:这是树中最长路径的长度。比如说你有一棵大树,从根到最远的叶子走了一圈,那这根最长的树干就是直径。
2. **单调队列**:一种数据结构,用来快速找到某个范围内的最大值或者最小值。就像是去超市购物,你是排在队伍里等着付款,单调队列就有点像你在观察谁最快结账,然后你立刻换队伍。

### 解决思路:
1. **先找到树的直径**。
   - 随意选一个起点,跑一次深度优先搜索(DFS),找出最远的节点(叫它 `p`)。
   - 然后以 `p` 为起点,再跑一次DFS,找到另一个最远的节点(叫它 `q`)。这样我们就有了树的直径。

2. **计算不过直径的最大距离**:
   - 对于直径上的每个节点,找出从它出发到不在直径上的节点的最远距离。这个距离记作 `val`。

3. **使用单调队列优化**:
   - 在直径上用单调队列找出每段路径的偏心距(即路径中所有节点的最大 `val` 值)。

### 代码讲解:
下面是你给出的代码的简化注释版本:

  1. #include <iostream>
  2. #include <vector>
  3. #include <algorithm>
  4. #include <cstring>

  5. using namespace std;

  6. const int N = 5e5+5;
  7. typedef long long ll;

  8. struct Edge {
  9.     int v, w;
  10. };

  11. int n;
  12. ll s;
  13. vector<Edge> e[N];
  14. int p, q, f[N];
  15. ll d[N];

  16. void dfs1(int u, int pa, int &id, ll dis) {
  17.     if (dis > d[id]) {
  18.         id = u;
  19.         d[id] = dis;
  20.     }
  21.     for (auto &ed : e[u]) {
  22.         if (ed.v == pa) continue;
  23.         dfs1(ed.v, u, id, dis + ed.w);
  24.     }
  25. }

  26. void findDiameter() {
  27.     memset(d, 0, sizeof(d));
  28.     p = 1; dfs1(p, 0, p, 0); // 找到直径的一端 p
  29.     memset(d, 0, sizeof(d));
  30.     dfs1(p, 0, q, 0); // 找到直径的另一端 q
  31. }

  32. ll max_dist_not_in_diameter;

  33. void dfs2(int u, int pa, ll dist) {
  34.     max_dist_not_in_diameter = max(max_dist_not_in_diameter, dist);
  35.     for (auto &ed : e[u]) {
  36.         if (ed.v == pa) continue;
  37.         dfs2(ed.v, u, dist + ed.w);
  38.     }
  39. }

  40. int main() {
  41.     ios::sync_with_stdio(false);
  42.     cin.tie(0);

  43.     cin >> n >> s;
  44.     for (int i = 1; i < n; i++) {
  45.         int x, y, z;
  46.         cin >> x >> y >> z;
  47.         e[x].push_back({y, z});
  48.         e[y].push_back({x, z});
  49.     }

  50.     // Step 1: Find the diameter
  51.     findDiameter();
  52.    
  53.     // Step 2: Calculate max distance not in diameter
  54.     max_dist_not_in_diameter = 0;
  55.     for (int i = 1; i <= n; ++i) {
  56.         if (d[i] == 0) { // means node i is not on the diameter
  57.             dfs2(i, 0, 0);
  58.         }
  59.     }

  60.     // Step 3: Traverse diameter and use sliding window (or monotone deque) to find minimum value
  61.     ll ans = LLONG_MAX;
  62.     for (int i = q, j = q; i; i = f[i]) {
  63.         while (f[j] != f[q] && d[i] - d[j] > s) j = f[j];
  64.         ans = min(ans, max({max_dist_not_in_diameter, d[i], d[q] - d[j]}));
  65.     }

  66.     cout << ans << endl;
  67.     return 0;
  68. }
复制代码


### 笑话时间:
想象一下,树的直径就像你每天上学的最长那条公交路线,保证无论多早起床,这条路总是最长的。而树的核呢,就像是你在公交车站等车的时候,发现哪里人少,能最快上车的地方。希望这种比喻能让你的编程之路不再那么枯燥,加油!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

头像被屏蔽
发表于 2024-6-26 20:36:57 | 显示全部楼层

回帖奖励 +2 鱼币

提示: 作者被禁止或删除 内容自动屏蔽
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

头像被屏蔽
发表于 2024-6-26 20:37:10 | 显示全部楼层

回帖奖励 +2 鱼币

提示: 作者被禁止或删除 内容自动屏蔽
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

头像被屏蔽
发表于 2024-6-26 20:37:22 | 显示全部楼层

回帖奖励 +2 鱼币

提示: 作者被禁止或删除 内容自动屏蔽
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

头像被屏蔽
发表于 2024-6-26 20:37:34 | 显示全部楼层

回帖奖励 +2 鱼币

提示: 作者被禁止或删除 内容自动屏蔽
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

头像被屏蔽
发表于 2024-6-26 20:37:45 | 显示全部楼层

回帖奖励 +2 鱼币

提示: 作者被禁止或删除 内容自动屏蔽
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

头像被屏蔽
发表于 2024-6-26 20:38:15 | 显示全部楼层

回帖奖励 +2 鱼币

提示: 作者被禁止或删除 内容自动屏蔽
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

头像被屏蔽
发表于 2024-6-26 20:38:25 | 显示全部楼层

回帖奖励 +2 鱼币

提示: 作者被禁止或删除 内容自动屏蔽
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

头像被屏蔽
发表于 2024-6-26 20:38:40 | 显示全部楼层

回帖奖励 +2 鱼币

提示: 作者被禁止或删除 内容自动屏蔽
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

头像被屏蔽
发表于 2024-6-26 20:38:58 | 显示全部楼层

回帖奖励 +2 鱼币

提示: 作者被禁止或删除 内容自动屏蔽
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2024-6-28 21:59:51 | 显示全部楼层

回帖奖励 +2 鱼币

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2024-7-1 14:20:16 | 显示全部楼层

回帖奖励 +2 鱼币

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2024-7-2 18:15:32 | 显示全部楼层

回帖奖励 +2 鱼币

6
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2024-7-3 19:08:14 | 显示全部楼层

回帖奖励 +2 鱼币

听懂掌声
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-7-6 23:11:00 | 显示全部楼层

回帖奖励 +2 鱼币

很不错
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-7-9 11:30:52 | 显示全部楼层

回帖奖励 +2 鱼币

心之碎片太好了
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-7-9 14:42:16 | 显示全部楼层

回帖奖励 +2 鱼币

我目前不懂,但
不影响我大为震撼
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-7-12 18:26:30 | 显示全部楼层
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2024-7-12 18:27:13 | 显示全部楼层
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-5-25 22:32

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表