#include<bits/stdc++.h> usingnamespace std; int n, x, y, z, pre[110], val[110], k; structnode { int to, len, nxt; } a[220]; voidadd(int u, int v, int l){ a[++k] = {v, l, pre[u]}; pre[u] = k; } intdfs(int x, int fa, int dis){ int res = dis * val[x]; for (int i = pre[x]; i; i = a[i].nxt) { if (a[i].to != fa) { res += dfs(a[i].to, x, dis + a[i].len); } } return res; } intmain(){ cin >> n; for (int i = 1; i <= n; i++) { int lc, rc; //left connect,right connect cin >> val[i] >> lc >> rc;//猎奇输入 if (lc) { add(i, lc, 1); add(lc, i, 1); } if (rc) { add(i, rc, 1); add(rc, i, 1); } } int ans = INT_MAX; for (int i = 1; i <= n; i++) { ans = min(ans, dfs(i, 0, 0)); } cout<<ans<<endl; }
树的直径
做法 1. 两次 DFS
过程
首先从任意节点 y 开始进行第一次 DFS,到达距离其最远的节点,记为 z,然后再从 z 开始做第二次 DFS,到达距离 z 最远的节点,记为 z′,则 δ(z,z′) 即为树的直径。
显然,如果第一次 DFS 到达的节点 z 是直径的一端,那么第二次 DFS 到达的节点 z′ 一定是直径的一端。我们只需证明在任意情况下,z 必为直径的一端。
定理:在一棵树上,从任意节点 y 开始进行一次 DFS,到达的距离其最远的节点 z 必为直径的一端。
int dep[2000010]; int pre[2000010], k, n; int x, y; voidadd(int u, int v){ a[++k] = {v, pre[u]}; pre[u] = k; } voiddfs(int x, int fa){ for (int i = pre[x]; i; i = a[i].nxt) { int to = a[i].to; if (to != fa) { dep[to] = dep[x] + 1; dfs(to, x); } } }
intmain(){ cin >> n; for (int i = 1; i < n; i++) { cin >> x >> y; add(x, y); add(y, x); } dep[1] = 0; dfs(1, 0); int ma = 0, mai; for (int i = 1; i <= n; i++) { if (dep[i] > ma) { ma = dep[i]; mai = i; } } dep[mai] = 0; dfs(mai, 0); int maxx = 0; for (int i = 1; i <= n; i++) { maxx = max(maxx, dep[i]); } cout << maxx; }
int dep[2000010]; int pre[2000010], k, n; int x, y; voidadd(int u, int v){ a[++k] = {v, pre[u]}; pre[u] = k; } voiddfs(int x, int fa){ for (int i = pre[x]; i; i = a[i].nxt) { int to = a[i].to; if (to != fa) { dep[to] = dep[x] + 1; dfs(to, x); } } }
intmain(){ cin >> n; for (int i = 1; i < n; i++) { cin >> x >> y; add(x, y); add(y, x); } dep[1] = 0; dfs(1, 0); int ma = 0, mai; for (int i = 1; i <= n; i++) { if (dep[i] > ma) { ma = dep[i]; mai = i; } } dep[mai] = 0; dfs(mai, 0); int maxx = 0; for (int i = 1; i <= n; i++) { maxx = max(maxx, dep[i]); } cout << maxx; }
/* 程序执行流程概述(核心逻辑:二分答案 + 树上差分 + LCA): 1. 输入处理:读取树的节点数、路径数,构建邻接表。 2. 预处理阶段: a. 第一次DFS(dfs):计算节点深度、父节点(倍增基础),并将边权"下沉"到子节点(用子节点唯一标识边)。 b. 构建倍增表(f数组):预处理每个节点的2^j级祖先,用于快速查询LCA。 c. 第二次DFS(dfs_dist):计算根节点到所有节点的距离(distRoot数组),用于快速计算路径长度。 3. 预计算路径信息:读取每条路径的起点/终点,计算其LCA和原始长度(pathLen数组),记录最大路径长度(用于二分边界)。 4. 二分答案:通过二分法寻找**最小的最大路径长度T**,使得存在一条边置零后,所有路径长度≤T。 a. 可行性检查(check函数): i. 统计超限路径(长度> T的路径)。 ii. 用树上差分标记这些路径覆盖的边。 iii. 汇总差分数组,得到每条边的覆盖次数。 iv. 检查是否存在边被所有超限路径覆盖,且边权足够大(置零后可使最大超限路径≤T)。 5. 输出结果:输出最小的T。 */ /* 关于卡常: #1 头文件 #2 for int -> for register int #3 void -> inline void #4 [!Important] 改二分逻辑(左边界)。见 https://images.cnblogs.com/cnblogs_com/blogs/838391/galleries/2441761/o_250722010513_1.png */ #include"bits/stdc++.h"//卡常#1 usingnamespace std; constint N = 3e5 + 10; // 最大节点数(适配题目3e5的数据范围) constint L = 20; // 倍增深度(2^20=1e6,足够覆盖3e5节点的深度)
// 全局变量定义(按功能分类) // 1. LCA与树结构 int f[N][L]; // 倍增数组:f[i][j]表示i的2^j级祖先 int pre[N]; // 邻接表头指针:pre[i]指向i的第一条边 int lg[N]; // 预处理log2值:lg[i] = floor(log2(i)),用于快速计算倍增层级 int n, k, q; // n:节点数;k:边计数;q:路径数 int dep[N]; // 节点深度:dep[i]表示i的深度(根节点1的深度为1)
// 2. 路径与差分 int st[N], ed[N]; // 路径的起点/终点:st[i]是第i条路径的起点,ed[i]是终点 longlong pathLen[N];// 路径原始长度:pathLen[i]是第i条路径的长度(未置零任何边) int pathLCA[N]; // 路径的LCA:pathLCA[i]是第i条路径的起点与终点的LCA int mark[N]; // 差分数组:用于标记路径覆盖的边,汇总后表示边的覆盖次数
// 3. 边权与距离 int base[N]; // 边权下沉数组:base[i]表示i到父节点的边的权值(根节点1无父边,base[1]无意义) longlong distRoot[N];// 根到节点的距离:distRoot[i]是根节点1到i的距离