当前位置: 首页 > news >正文

点分治解析

点分治是一种处理树上路径的方法,诸如询问树上长度为k的路径是否存再等问题。

对于这类问题,如果给定起点和终点让我们求路径长度,显然用LCA就好。但有些题目没有给定的起点和终点,如果强行使用LCA,就会得到O(n^2logn)的时间复杂度。当然,这时候也可以直接挨个点暴力DFS遍历,那样也会得到O(n^2)的时间复杂度。

想要得到更优的时间复杂度,就要用到点分治。点分治和直接暴力遍历的最大区别就是:不遍历所有点,而是遍历重心。重心有一个性质,以树的重心为根时,所有子树的大小都不超过树的大小的一半。由此,就可以对时间复杂度进行优化,达到O(nlogn)。接下来,就来思考如和通过重心来遍历。

首先我们要知道如何求重心,代码如下

void getroot(int u, int f) {siz[u] = 1, maxv[u] = 0;
//maxv[u]必须更新为0,因为要求多次重心for (const auto& [v, w] : g[u]) {if (vis[v] || v == f)continue;getroot(v, u);siz[u] += siz[v];maxv[u] = max(maxv[u], siz[v]);}maxv[u] = max(maxv[u], sum - siz[u]);
//使用sum并更新以正确限制范围if (maxv[u] < maxv[root])root = u;
}

另外,还需要从重心开始遍历,得到其子树中所有节点到重心的距离,代码如下

void getdis(int u, int f, int d) {if (d > NK)return;//NK之后解释dis.push_back(d);//dis存,之后会清空for (const auto& [v, w] : g[u]) {if (vis[v] || v == f)continue;getdis(v, u, d + w);}
}

有了些前置处理后,当K的上限比较小时,我们可以用桶来存路径的长度,当两段路径到一个点的路径之和等于所需值时即可,这也是该算法的关键。详细见代码。

模板题链接https://www.luogu.com.cn/problem/P3806

///P3806  点分治模板  k较小可以用桶优化 复杂度为o(nlogn)
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int INF = 0x3f3f3f3f;
const int N = 1e4 + 10;
const int NK = 1e7 + 10;
//NK为K的最大值,可以优化代码,防止产生错误
vector<pair<int, int>>g[N];
bool vis[N], cnt[NK], ans[N];//vis数组防止重复查找重心
//cnt是桶,ans用来离线处理答案
int n, m, sum, root;
vector<int>dis, q;
int siz[N], maxv[N];//求重心数组void getroot(int u, int f) {siz[u] = 1, maxv[u] = 0;for (const auto& [v, w] : g[u]) {if (vis[v] || v == f)continue;getroot(v, u);siz[u] += siz[v];maxv[u] = max(maxv[u], siz[v]);}maxv[u] = max(maxv[u], sum - siz[u]);if (maxv[u] < maxv[root])root = u;
}void getdis(int u, int f, int d) {if (d > NK)return;dis.push_back(d);for (const auto& [v, w] : g[u]) {if (vis[v] || v == f)continue;getdis(v, u, d + w);}
}void calc(int u) {vector<int>tot;//tot数组用来清空桶,如果不用tot数组,就要一次清理1e7的数据cnt[0] = 1;//一定要将0标记上for (const auto& [v, w] : g[u]) {if (vis[v])continue;dis.clear();getdis(v, u, w);for (int i = 0; i < m; i++) {if (ans[i])continue;//离线做法for (int d : dis) {if (d > q[i])continue;//显然不可能if (cnt[q[i] - d]) {ans[i] = 1;break;}}}//更新桶for (int d : dis) {if (d > NK || cnt[d])continue;cnt[d] = 1;tot.push_back(d);}}//清空桶for (int d : tot) {cnt[d] = 0;}
}void work(int u) {//logn的由来 不断寻找重心,从重心开始遍历vis[u] = 1;calc(u);for (const auto& [v, w] : g[u]) {if (vis[v])continue;sum = siz[v];root = 0, maxv[root] = INF;getroot(v, 0);work(root);}
}int main() {ios::sync_with_stdio(false);cin.tie(nullptr); cout.tie(nullptr);cin >> n >> m;for (int i = 1; i < n; i++) {int u, v, w;cin >> u >> v >> w;g[v].emplace_back(u, w);g[u].emplace_back(v, w);}q.resize(m);for (int i = 0; i < m; i++) {cin >> q[i];}sum = n;root = 0, maxv[root] = INF;getroot(1, 0);work(root);for (int i = 0; i < m; i++) {cout << (ans[i] ? "AYE" : "NAY") << endl;}return 0;
}

http://www.xdnf.cn/news/301375.html

相关文章:

  • Spark,配置hadoop集群1
  • Spring AI Alibaba-03- Spring AI + DeepSeek-R1 + ES/Milvus + RAG 智能对话应用开发全流程
  • 从黔西游船侧翻事件看极端天气预警的科技防线——疾风气象大模型如何实现精准防御?
  • 微服务框架中@FeignClient远程调用,请求无法携带问题处理
  • 【工具】解析URL获取实际图片地址下载原始FFHQ图像
  • 如何将本地 Jar 包安装到 Maven 仓库(以 Aspose 为例)
  • 小芯片大战略:Chiplet技术如何重构全球半导体竞争格局?
  • aws平台windows虚拟机扩容
  • Eigen矩阵的平移,旋转,缩放
  • 制造企业PLM系统成本基准:2025年预算分配与资源成本率的5种优化模型
  • AI智能体|扣子(Coze)实战【天气查询插件开发教程】
  • IAA-Net:一种实孔径扫描雷达迭代自适应角超分辨成像方法——论文阅读
  • centos的根目录占了大量空间怎么办
  • nut-list和nut-swipe搭配:nut-cell侧滑定义无法冒泡打开及bug(含代码、案例、截图)
  • 高并发PHP部署演进:从虚拟机到K8S的DevOps实践优化
  • 1. 视频基础知识
  • Java高频面试之并发编程-12
  • 详细教程:如何在vs code里面给普通的HTML搭建局域网服务器给其他设备访问
  • react-14defaultValue(仅在首次渲染时生效)和value(受 React 状态控制)
  • vue项目中渲染markdown并处理报错
  • Electrolink信息泄露(CVE-2025-28228)
  • 图像处理软件imgPro—调参救星!
  • RabbitMq(尚硅谷)
  • 常识补充(NVIDIA NVLink技术:打破GPU通信瓶颈的革命性互联技术)
  • 【quantity】1 SI Prefixes 实现解析(prefix.rs)
  • 当手机开始预判你的下一步:一场正在颠覆生活的AI静默革命
  • 帕累托最优提示 是什么
  • Java 中的数据结构--简单汇总
  • 状态模式 VS 策略模式
  • Ubuntu开放端口