点分治详解

本文介绍了点分治算法,并通过一个求解树上长度不超过K的路径数量的问题来阐述其应用。首先介绍了分治思想,然后详细讲解了点分治在树上解决问题的思路,包括暴力解法、点分治优化以及如何处理特殊情况,如树退化为链的情况。通过使用重心作为根节点,将时间复杂度优化至O(nlog2n)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天做了一道点分治的题目,所以就去网上学了一下。

相信大家都听说过“分治”吧,分治就是“分而治之”一般是把n分成2份,然后再对每一份进行相同的操作,最后合并起来。

而点分治,一般情况下是在一棵树上面进行分治,和普通的分治大同小异。

先看一道例题

【题意】
给定一个有N个点(编号1,2,…,N)的树,每条边都有一个权值(不超过1000)。
树上两个节点x与y之间的路径长度就是路径上各条边的权值之和。
求长度不超过K的路径有多少条。

poj 1741 – tree
【输入格式】
输入包含多组测试样例。
每组测试样例的第一行包含两个正整数N和K。
接下来N-1行,每行包含三个正整数u,v,l,表示节点u与v之间存在一条边,且边的权值为l。
当输入样例N=0,K=0时,表示输入终止,且该样例无需处理。
【输出格式】
每个测试样例输出一个结果。
每个结果占一行。

【数据范围】
N≤10000,K≤10^7
【输入样例】
5 4
1 2 3
1 3 1
1 4 2
3 5 1
0 0
【输出样例】
8

先是考虑暴力的解法,直接枚举每一个点,求lca,然后判断是否<=k,可以得10分

仔细想想,对于一个点来说,长度<=k的路径无非就只有两种:
1.经过该点
2.不经过该点
所以我们采取点分治的做法,先选1作为根节点,求出经过1且<=k的路径条数,然后删掉1,再对1的每个儿子进行重复操作
那么我们怎么求出路径的条数呢
设d[i]为i到根节点root的距离,其中d[root]=0
搜索求出d,对d从小到大排序
然后我们令指针l = 1, r = tp (tp为d中,点的个数)
如果 d[l] + d[r] <= k ,结果就加上r-l+1,然后l++
否则r- -
但是这样会出现一个问题,比如son是root的一个儿子,在son的子树中,有很多个加起来满足<=k的,而这些都不经过u,怎么办???
容斥原理!!!
对于每个儿子,令它的d不变,统计一次<=k的个数,从ans里面减去这个数就好了。
这样可以取得70分

为什么只有70呢
举个例子吧,如果树退化成链的话,这个做法就和暴力没有区别,时间复杂度O(n2logn)O(n^2logn)O(n2logn)
怎么办呢,因为这是一棵无根树,每一个点成为根节点都不会影响结果,所以我们每次都从当前的树种求出它的重心作为root,这样的话,无论树怎么样,都只有lognlognlogn层,时间复杂度O(nlog2n)O(nlog^2n)O(nlog2n)

在这里插入图片描述
这是形象的搜索过程

参考代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 10006;

int n, m, k, all, ans, root;
/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值