男人八题-点分治-Acwing252.树

news/2025/2/10 18:32:16/文章来源:https://www.cnblogs.com/ayanami--/p/18708542

Acwing252.树

题目分析

树中的路径分为三种

  • 路径两端在同一个子树
  • 路径两端在不同子树
  • 路径有一端是重心

因此可以分情况处理, 对于第一种情况可以进行递归处理, 第二种情况需要使用容斥原理求得(下面重点介绍), 第三种情况枚举重心到其他节点的路径就可以求得

代码分析

求子树大小

int get_size(int _index, int pre) {
//	如果当前子树被删除, 返回0if (visited[_index]) return 0;int res = 1;for (int i = head[_index]; ~i; i = next_edge[i]) {int ver = edge_end[i];if (ver == pre) continue;res += get_size(ver, _index);}return res;
}

求当前点出发的子树的所有路径

void get_dist(int _index, int pre, int dis, int &ptr) {if (visited[_index]) return;arr2[ptr++] = dis;for (int i = head[_index]; ~i; i = next_edge[i]) {int ver = edge_end[i];if (ver == pre) continue;get_dist(ver, _index, dis + w[i], ptr);}
}

求树的重心

int get_weight_center(int _index, int pre, int tot, int &res) {if (visited[_index]) return 0;int sum = 1, max_son = 0;for (int i = head[_index]; ~i; i = next_edge[i]) {int ver = edge_end[i];if (ver == pre) continue;int size = get_weight_center(ver, _index, tot, res);
//		更新最大子树大小max_son = max(max_son, size);sum += size;}//	总数减下面的子树总和, 就是当前点所在父节点的子树大小max_son = max(max_son, tot - sum);
//	如果最大子树大小 <= tot / 2, 那么当前点可以作为重心if (max_son <= tot >> 1) res = _index;return sum;
}

注意:这里树的重心只要子树的大小<= n / 2就可以保证点分治时间复杂度

求单个子树中路径和 <= k的路径组合数量

int get(int arr[], int cnt) {sort(arr, arr + cnt);int res = 0;for (int i = cnt - 1, j = -1; i >= 0; --i) {
//		找到满足条件的最大jwhile (j + 1 < i && arr[j + 1] + arr[i] <= k) j++;j = min(j, i - 1);res += j + 1;}return res;
}

双指针算法求解

核心函数

int calc(int _index) {if (visited[_index]) return 0;int res = 0;//	找到当前子树的重心get_weight_center(_index, -1, get_size(_index, -1), _index);visited[_index] = true;int p1 = 0;for (int i = head[_index]; ~i; i = next_edge[i]) {int ver = edge_end[i], p2 = 0;get_dist(ver, -1, w[i], p2);
//		减去一个子树中的组合情况res -= get(arr2, p2);//		将子树的距离加入到去全局的距离中for (int j = 0; j < p2; ++j) {
//			某个点是重心的情况if (arr2[j] <= k) res++;arr1[p1++] = arr2[j];}}//	加上子树内部的组合情况res += get(arr1, p1);
//	递归处理子树for (int i = head[_index]; ~i; i = next_edge[i]) {int ver = edge_end[i];res += calc(ver);}return res;
}

先找到当前子树的重心, 然后将重心标记

然后将路径分为三种情况, 上面有论述,
直接看最复杂的情况, 也就是两个点分别在两个子树中

  • 求得子树中的路径组合, 因为是不合法的, 因此将其从答案中减去
  • 然后将子树的路径添加到全局的路径当中
  • 在添加过程中不要忘记记录子树到重心也是一种情况
  • 最后加入到res的就是全局的除去子树之间的路径组合

最后处理第一种和第三种情况

时间复杂度分析

递归过程中一共$\log{}{n}$层, 每一层点数不超过$n$, 排序时间复杂度$O(n\log_{}{n})$, 因此总的时间复杂度$O(n\log_{}{n}^{2})$

完整代码

#include <iostream>
#include <algorithm>
#include <cstring>using namespace std;const int N = 10010, EDGE_NUMBER = N << 1;int n, k;
int head[N], edge_end[EDGE_NUMBER], next_edge[EDGE_NUMBER], w[EDGE_NUMBER], edge_index;
//节点是否被删除
bool visited[N];
//当前重心的所有子树的距离, 当前子树的距离
int arr1[N], arr2[N];void add(int ver1, int ver2, int val) {edge_end[edge_index] = ver2, next_edge[edge_index] = head[ver1], w[edge_index] = val, head[ver1] = edge_index++;
}//求子树大小
int get_size(int _index, int pre) {
//	如果当前子树被删除, 返回0if (visited[_index]) return 0;int res = 1;for (int i = head[_index]; ~i; i = next_edge[i]) {int ver = edge_end[i];if (ver == pre) continue;res += get_size(ver, _index);}return res;
}//求从当前点出发所有路径的长度
void get_dist(int _index, int pre, int dis, int &ptr) {if (visited[_index]) return;arr2[ptr++] = dis;for (int i = head[_index]; ~i; i = next_edge[i]) {int ver = edge_end[i];if (ver == pre) continue;get_dist(ver, _index, dis + w[i], ptr);}
}//求树的重心, 返回值是当前子树大小
int get_weight_center(int _index, int pre, int tot, int &res) {if (visited[_index]) return 0;int sum = 1, max_son = 0;for (int i = head[_index]; ~i; i = next_edge[i]) {int ver = edge_end[i];if (ver == pre) continue;int size = get_weight_center(ver, _index, tot, res);
//		更新最大子树大小max_son = max(max_son, size);sum += size;}//	总数减下面的子树总和, 就是当前点所在父节点的子树大小max_son = max(max_son, tot - sum);
//	如果最大子树大小 <= tot / 2, 那么当前点可以作为重心if (max_son <= tot >> 1) res = _index;return sum;
}int get(int arr[], int cnt) {sort(arr, arr + cnt);int res = 0;for (int i = cnt - 1, j = -1; i >= 0; --i) {
//		找到满足条件的最大jwhile (j + 1 < i && arr[j + 1] + arr[i] <= k) j++;j = min(j, i - 1);res += j + 1;}return res;
}int calc(int _index) {if (visited[_index]) return 0;int res = 0;//	找到当前子树的重心get_weight_center(_index, -1, get_size(_index, -1), _index);visited[_index] = true;int p1 = 0;for (int i = head[_index]; ~i; i = next_edge[i]) {int ver = edge_end[i], p2 = 0;get_dist(ver, -1, w[i], p2);
//		减去一个子树中的组合情况res -= get(arr2, p2);//		将子树的距离加入到去全局的距离中for (int j = 0; j < p2; ++j) {
//			某个点是重心的情况if (arr2[j] <= k) res++;arr1[p1++] = arr2[j];}}//	加上子树内部的组合情况res += get(arr1, p1);
//	递归处理子树for (int i = head[_index]; ~i; i = next_edge[i]) {int ver = edge_end[i];res += calc(ver);}return res;
}int main() {while (scanf("%d%d", &n, &k), n && k) {memset(visited, false, sizeof visited);memset(head, -1, sizeof head);edge_index = 0;for (int i = 0; i < n - 1; ++i) {int ver1, ver2, val;scanf("%d%d%d", &ver1, &ver2, &val);add(ver1, ver2, val);add(ver2, ver1, val);}printf("%d\n", calc(0));}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/881791.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【验证码逆向专栏】最新某验四代动态参数逆向详解

声明 本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关! 本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术…

DeepSeek:AI界的“东方海啸”

嘿,大家有没有听说过DeepSeek?这个AI界的新星正在掀起一场“东方海啸”!让我们一起来看看它是如何改变游戏规则的。 一、技术革新:从参数到生态 想象一下,一个AI模型的参数从2360亿飙升到6710亿,训练成本却只有557.6万美元!这就是DeepSeek的魔力。你觉得这有多疯狂? 他…

007 数组扩展_新增方法

1、Array.from()Array.from方法用于将数组转为真正的数组温馨提示:常见的类数组有三类:①arguments②元素集合③类似数组的对象①argumentsfunction add(){let collect =Array.from(arguments);collect.push(40);console.log(collect);}add(10,20,30)②元素集合let divs=docu…

360度绩效考核法实例分析

某公司打算在2025年的上半年引进360度绩效考核体系,于是公司管理者就命令人力资源部制定出相应的考核体系实施方案。人力资源部根据360度绩效考核体系实施要点,并结合本公司内部实际情况,制定出了一套适用于本公司的360度绩效考核体系,具体内容如下: (一)确定考核队伍 考…

idea 远程断点

jar启动命令添加以下命令 0.0.0.0:端口号 -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=0.0.0.0:5005idea中新增远程jvm调试

Star 4w+,Apache Dubbo 3.3 全新发布,Triple X 领衔,开启微服务通信新时代

在 Apache Dubbo 突破 4w Star 之际,Apache Dubbo 团队正式宣布,Dubbo 3.3 正式发布!作为全球领先的开源微服务框架,Dubbo 一直致力于为开发者提供高性能、可扩展且灵活的分布式服务解决方案。此次发布的 Dubbo 3.3,通过 Triple X 的全新升级,突破了以往局限,实现了对南…

1次搞懂大模型备案被驳回原因!

大模型备案被驳回的原因一、语料安全不合规 1、语料来源未合法授权 语料需确保来源合法,包括开源协议、商业授权或自采数据的合规性。若语料中包含未授权的版权内容(如使用他人知识产权作品作为训练数据),或涉及未获授权的个人信息,将直接导致备案被驳回。 示例:某企业因…

内外网安全文件交换系统:突破网络屏障,筑牢数据安全防线

内外网安全文件交换系统:突破网络屏障,筑牢数据安全防线在数字化浪潮蓬勃发展的当下,企业对于数据安全保障以及高效文件传输机制的需求日益凸显。特别是在内外网实施物理隔离的网络架构下,如何达成安全、便捷的文件交换操作,已然成为众多企业亟待攻克的关键难题。本内外网…

利用排列序列提升游戏动画的真实感

在游戏开发中,我们经常需要处理重复的动画序列,以增加游戏的真实感。例如,在海战游戏中,我们可能会有一组军舰停泊在港湾,它们的桅杆或甲板时不时地需要摇晃,以模拟海风的影响。实现这种效果有多种方法,例如完全随机选择某艘军舰播放动画,或者按照固定的顺序依次播放。…

最小表示(字符串和树)学习笔记

字符串的最小表示法 由来 字符串有时需要进行“旋转”,即一个一个把尾部元素放到前面。那么,我们怎么知道一个字符串是不是另一个字符串旋转过来的呢? 我们发现,这种旋转其实相当于把字符串首尾相接成环,然后取不同的起点重新组成字符串。在这些能够组成的字符串中,字典序…

破解风电远程监控难题:EtherNet/IP转Modbus TCP配置大揭秘

EtherNet/IP转Modbus TCP在新能源风力发电场远程监控与数据分析的配置案例一、案例背景 在风力发电场的中控室安装捷米特JM-EIPM-TCP网关,连接到风电场的局域网。确保风力发电机组的控制系统、变流器等采用 MODBUS TCP 协议的设备以及基于 EtherNet/IP 协议的远程监控系统和数…

【字符串处理】构造长度为4的字符串,不足4位的用0补够4位

关键函数 insert(pos,length, ch) string num = to_string(i); num.insert(0, 4 - num.size(), 0);例题:猜数字样例1 输入 6 4815 1A1B 5716 0A1B 7842 0A1B 4901 0A0B 8585 3A0B 8555 2A1B输出 3585C++代码 #include <cstring> #include <iostream> #include <…