【P4178】Tree——点分治
(题面来自luogu)
题目描述
给你一棵TREE,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K
输入格式
N(n<=40000) 接下来n-1行边描述管道,按照题目中写的输入 接下来是k
输出格式
一行,有多少对点之间的距离小于等于k
原本是点分治的模版题,从昨晚调到今晚……这里记录下点分治实现时需要注意的几个细节。
1、分治过程中递归子树大小的确定
以下是点分治过程的核心函数,其中cur表示以u为根进行分治的树的大小。
- void Divide(int u) {
- vis[u] = true;
- ans += Solve(u, 0);
- int tcur = cur;
- for (int i = head[u]; i; i = edge[i].nxt) {
- int v = edge[i].to;
- if (vis[v]) continue;
- ans -= Solve(v, edge[i].w);
- Mn = inf;
- //cur = size[v];
- cur = size[u] > size[v] ? size[v] : tcur - size[u];
- Find_rt(v, u);
- Divide(root);
- }
- }
重点在第10、11行递归子树大小确定的两种写法,其中第11行未注释的版本是正确的。考虑到我们每次在当前树中选重心为根进行分治,那么u并不一定是该树在搜索树意义下的根节点。也就是说,u的子节点v有可能是u在搜索树上的父亲,因此在确定递归子树大小时加入一个特判。因为cur的值会因为遍历先前的v而改变,我们在第4行用一个新变量记录当前树的大小。这个就是调了一天的锅的出处
(不过据说不加这个判断复杂度也不会劣化……貌似还有人证明了,不过保证正确性显然是好的)
2、关于点分治两种写法的优劣
点分治不同写法的讲解请见我的博客:https://www.cnblogs.com/TY02/p/11203163.html
之前认为用容斥算两遍的做法常数过大,比较起来把子树分开互相统计更好。实际上第二种做法有它的局限性:例如在这个题中,暴力枚举每条路径会T飞,我们只能把u子树中所有的节点深度都统计一遍,排序后利用单调性用双指针统计答案。这就暴露了分子树统计的劣势,它只可以把子树中两点不重不漏地两两枚举、组合路径信息,无法在其中嵌套别的操作。容斥的优点在于它把所有的节点信息一次性统计出来,适合类似本题利用数据单调性排序来统计的情形。这个题也不排序也可以用权值树状数组来做,复杂度相同,常数因为要清空数组会大一些。
完整代码:
- #include <iostream>
- #include <cstdio>
- #include <cstring>
- #include <algorithm>
- #define BUG puts("$$$")
- #define rint register int
- #define maxn 40010
- typedef long long ll;
- using namespace std;
- const int inf = (int)1e9;
- template <typename T>
- void read(T &x) {
- x = 0;
- char ch = getchar();
- // int f = 1;
- while (!isdigit(ch)) {
- // if (ch == '-') f = -1;
- ch = getchar();
- }
- while (isdigit(ch)) {
- x = x * 10 + (ch ^ 48);
- ch = getchar();
- }
- // x *= f;
- }
- int n, k;
- int ans = 0;
- int head[maxn], top;
- struct E {
- int to, nxt, w;
- } edge[maxn << 1];
- inline void insert(int u, int v, int w) {
- edge[++top] = (E) {v, head[u], w};
- head[u] = top;
- }
- bool vis[maxn];
- int size[maxn], root, Mn, cur;
- void Find_rt(int u, int pre) {
- size[u] = 1;
- int Mxson = 0;
- for (int i = head[u]; i; i = edge[i].nxt) {
- int v = edge[i].to;
- if (v == pre || vis[v]) continue;
- Find_rt(v, u);
- size[u] += size[v];
- Mxson = max(Mxson, size[v]);
- }
- Mxson = max(Mxson, cur - size[u]);
- if (Mn > Mxson)
- root = u, Mn = Mxson;
- }
- int chd[maxn], tot;
- void calc(int u, int pre, int d) {
- chd[++tot] = d;
- if (d >= k) return;
- for (int i = head[u]; i; i = edge[i].nxt) {
- int v = edge[i].to;
- if (v == pre || vis[v]) continue;
- calc(v, u, d + edge[i].w);
- }
- }
- int Solve(int u, int extra) {
- int ret = 0;
- tot = 0;
- calc(u, 0, extra);
- sort(chd + 1, chd + 1 + tot);
- rint l = 1, r = tot;
- while (l < r)
- chd[l] + chd[r] <= k ? (ret += (r - l), ++l) : (--r);
- return ret;
- }
- void Divide(int u) {
- vis[u] = true;
- ans += Solve(u, 0);
- int tcur = cur;
- for (int i = head[u]; i; i = edge[i].nxt) {
- int v = edge[i].to;
- if (vis[v]) continue;
- ans -= Solve(v, edge[i].w);
- Mn = inf;
- //cur = size[v];
- cur = size[u] > size[v] ? size[v] : tcur - size[u];
- Find_rt(v, u);
- Divide(root);
- }
- }
- int main() {
- read(n);
- int u, v, w;
- for (int i = 1; i < n; ++i) {
- read(u), read(v), read(w);
- insert(u, v, w), insert(v, u, w);
- }
- read(k);
- Mn = inf, cur = n;
- Find_rt(1, 0);
- Divide(root);
- printf("%d", ans);
- return 0;
- }
【P4178】Tree——点分治的更多相关文章
- 洛谷P4178 Tree (点分治)
题目描述 给你一棵TREE,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K 输入输出格式 输入格式: N(n<=40000) 接下来n-1行边描述管道,按照题目中写的输入 接下 ...
- 洛谷 P4178 Tree —— 点分治
题目:https://www.luogu.org/problemnew/show/P4178 这道题要把 dep( dis? ) 加入一个 tmp 数组里,排序,计算点对,复杂度很美: 没有写 sor ...
- P4178 Tree 点分治
思路:点分治 提交:1次 题解: 要求权值和\(\leq K\) 的路径,我们可以类比点分治的模板,把长为\(len\)是否存在,改为\(len\)的路径的条数,并用用树状数组维护前缀和,这样就可以求 ...
- [洛谷P4178] Tree (点分治模板)
题目略了吧,就是一棵树上有多少个点对之间的距离 \(\leq k\) \(n \leq 40000\) 算法 首先有一个 \(O(n^2)\) 的做法,枚举每一个点为起点,\(dfs\) 一遍可知其它 ...
- [Luogu P4178]Tree (点分治+splay)
题面 传送门:https://www.luogu.org/problemnew/show/P4178 Solution 首先,长成这样的题目一定是淀粉质跑不掉了. 考虑到我们不知道K的大小,我们可以开 ...
- POJ1471 Tree/洛谷P4178 Tree
Tree P4178 Tree 点分治板子. 点分治就是直接找树的重心进行暴力计算,每次树的深度不会超过子树深度的\(\frac{1}{2}\),计算完就消除影响,找下一个重心. 所以伪代码: voi ...
- luogu P4178 Tree
题目链接 luogu P4178 Tree 题解 点分治 代码 // luogu-judger-enable-o2 #include<cstdio> #include<algorit ...
- 【题解】[P4178 Tree]
[题解]P4178 Tree 一道点分治模板好题 不知道是不是我见到的题目太少了,为什么这种题目都是暴力开值域的桶QAQ?? 问点对,考虑点分治吧.直接用值域树状数组开下来,统计的时候直接往树状数组里 ...
- P4178 Tree(点分治)
题面要求小于等于K的路径数目,我么很自然的想到点分治(不会的就戳我) 这道题的统计答案与模板题不一样的地方是由等于K到小于等于K 那么我们可以把每一个子节点到当前根(重心)的距离排序,然后用类似双指针 ...
随机推荐
- MySql与Oracle的几个主要区别
转自:https://blog.csdn.net/c332472988/article/details/52804078 一.并发性 并发性是oltp数据库最重要的特性,但并发涉及到资源的获取.共享与 ...
- Java8新特性探索之Lambda表达式
为什么引入Lambda表达式? Lambda 表达式产生函数,而不是类. 在 JVM(Java Virtual Machine,Java 虚拟机)上,一切都是一个类,因此在幕后执行各种操作使 lamb ...
- Eureka+Hystrix(断路器、熔断器)
红圈是断路器的三种状态: 关闭:1.当consumer访问provider时,在网络超时访问内,访问成功: 2.有时互相调用会出现网络涌动,(比如北京访问广东的服务器要经过很多次路由才能达到并相应), ...
- Educational Codeforces Round 97 (Rated for Div. 2)
补了一场Edu round. A : Marketing Scheme 水题 #include <cstdio> #include <algorithm> typedef lo ...
- 4G DTU是什么?
要从任何设备(个人计算机.平板电脑或智能手机)访问Internet,需要DTU或热点.大多数宽带和移动DTU在"4G"或第四代网络系统上运行.虽然互联网连接的许多基本原则与4G D ...
- 关于DevOps的七大误解,99%的人都曾中过招!
[摘要] DevOps方法可以为组织带来显著的积极影响,降低成本.提高效率,使开发团队的工作更加精简.为了掌握这个过程的优势,有必要认识到DevOps是什么.不是什么.在本文中,就将讨论一些流传甚广的 ...
- 如何使用捷映App制作朋友圈九宫格视频图片合集
在刷朋友圈的过程中,看到九宫格的视频图片合集,带有酷炫的音效和动效,展现效果非常好,那么我们该如何制作呢?下面我为大家介绍. 第一步,我们需要在各大应用市场搜索[捷映视频制作]App,点击[安装]. ...
- adb、package及activity
1. adb adb连接手机参考:https://www.cnblogs.com/mind18/p/12592252.html,中的三.5节 1.1. adb介绍 ADB全名Andorid De ...
- 【Kata Daily 190912】Alphabetical Addition(字母相加)
题目: Your task is to add up letters to one letter. The function will be given a variable amount of ar ...
- WEB系统防退出账户,回退主页问题(2020最新最有效的方式没有之一)
WEB系统防退出账户,回退主页问题(2020最新最有效的方式没有之一) 很多小伙伴在web开发中都遇倒的问题? JavaWeb项目注销后,可能存在通过浏览器缓存回退的方式进入主页系统 WEB项目 ...