树链剖分之点剖分(点分治)讲解
当一个问题可以分解成小问题时,我们一般可以采用分治算法,比如最简单的快速排序,就是分治算法的一个典型的应用。
那么处理树的问题时,假设求解满足条件的点对儿个数,对于一个树来说,两个点对儿的存在只能有两种情况,就是点对之间的路径过根和不过根,那么对于不过根的情况递归做,对于一棵树只考虑过根的情况,计算就行了。
以一个基础题为例子bzoj 2152
http://61.187.179.132/JudgeOnline/problem.php?id=2152
题的大概意思就是给定一棵树,求满足两点路径和为3的倍数的点对儿数量。
现在说下具体的实现,首先3的倍数可以转化为边长和mod3为0的限制,假设我们任意取一点为根,将无根树转化为有根树,那么对于这棵树来说,我们可以由一遍dfs求出各个子节点子树中到根节点长度为0,1,2的数量,那么这样就比较好转移了,记录一个b数组,假设当前访问到p子节点,那么b数组中保留的为p点之前访问过的子节点中各个长度的数量,记录一个sum数组为当前p节点中的长度情况,那么由sum[i]*b[3-i]和sum[0]*b[0]来更新答案就行了。
但是这样的话不能保证时间复杂度,在链的时候会退化到n^2。为了防止这样的情况发生,每次找根节点的时候不能任意选取,而是选取树的重心,这里的树的重心是指
去掉这个节点后,剩下的子树中点数最多的最小,直观的理解就是这个点附近的点比较密集,假设一颗数中有N个点,这样的点去掉后剩下的子树中的点数最多的不会超过floor(N/2),那么每一个节点为根时,这个子树中的节点会被访问一遍,因为每次找的是重心,每次都是floor(n/2),所以每个点最多会被访问log2N次,所以时间复杂度
是Nlog2N的。
具体的时间复杂度的证明可以去看2009年漆子超的论文《分治算法在树的路径问题中的应用》
找重心的过程类似于DP的过程
procedure getroot(u:longint); varms, s, x, p, q :longint;i :longint; begintop:=0;dfs_size(u,0);//这个操作是处理出子树的size值ms:=maxlongint;for i:=1 to top do beginx:=stack[i];s:=size[u]-size[x];q:=last[x];while q<>0 dobeginp:=other[q];if (ff[q]) or (p=yy[x]) then beginq:=pre[q];continue;end;if size[p]>s then s:=size[p];q:=pre[q];end;if s<ms then beginms:=s;root:=x;end;end; end;
这道题的pascal代码
1 /**************************************************************2 Problem: 21523 User: BLADEVIL4 Language: Pascal5 Result: Accepted6 Time:564 ms7 Memory:1360 kb8 ****************************************************************/9 10 //By BLADEVIL11 var12 n :longint;13 pre, other, len :array[0..40010] of longint;14 last :array[0..20010] of longint;15 l, top :longint;16 size, stack, yy :array[0..20010] of longint;17 ff :array[0..40010] of boolean;18 root :longint;19 b, sum :array[0..10] of longint;20 ans :longint;21 22 function gcd(x,y:longint):longint;23 begin24 if x<y then exit(gcd(y,x)) else25 if y=0 then exit(x) else exit(gcd(y,x mod y));26 end;27 28 procedure connect(x,y,z:longint);29 begin30 inc(l);31 pre[l]:=last[x];32 last[x]:=l;33 other[l]:=y;34 len[l]:=z;35 end;36 37 procedure dfs_size(x,fa:longint);38 var39 q, p :longint;40 begin41 size[x]:=1;42 inc(top);43 stack[top]:=x;44 q:=last[x];45 while q<>0 do46 begin47 p:=other[q];48 if (ff[q]) or (p=fa) then49 begin50 q:=pre[q];51 continue;52 end;53 dfs_size(p,x);54 inc(size[x],size[p]);55 q:=pre[q];56 end;57 yy[x]:=fa;58 end;59 60 procedure getroot(u:longint);61 var62 ms, s, x, p, q :longint;63 i :longint;64 begin65 top:=0;66 dfs_size(u,0);67 ms:=maxlongint;68 for i:=1 to top do69 begin70 x:=stack[i];71 s:=size[u]-size[x];72 q:=last[x];73 while q<>0 do74 begin75 p:=other[q];76 if (ff[q]) or (p=yy[x]) then77 begin78 q:=pre[q];79 continue;80 end;81 if size[p]>s then s:=size[p];82 q:=pre[q];83 end;84 if s<ms then85 begin86 ms:=s;87 root:=x;88 end;89 end;90 end;91 92 procedure dfs_value(x,fa,lz:longint);93 var94 q, p :longint;95 begin96 inc(sum[lz mod 3]);97 q:=last[x];98 while q<>0 do99 begin 100 p:=other[q]; 101 if (p=fa) or (ff[q]) then 102 begin 103 q:=pre[q]; 104 continue; 105 end; 106 dfs_value(p,x,lz+len[q]); 107 q:=pre[q]; 108 end; 109 end; 110 111 procedure solve(u:longint); 112 var 113 i, q, p :longint; 114 115 begin 116 getroot(u); 117 if top=1 then exit; 118 fillchar(b,sizeof(b),0); 119 b[3]:=1; 120 top:=0; 121 q:=last[root]; 122 while q<>0 do 123 begin 124 p:=other[q]; 125 if ff[q] then 126 begin 127 q:=pre[q]; 128 continue; 129 end; 130 fillchar(sum,sizeof(sum),0); 131 dfs_value(p,root,len[q]); 132 for i:=0 to 3 do ans:=ans+b[i]*sum[3-i]; 133 ans:=ans+sum[0]*b[0]; 134 for i:=0 to 3 do inc(b[i],sum[i]); 135 q:=pre[q]; 136 end; 137 138 q:=last[root]; 139 while q<>0 do 140 begin 141 p:=other[q]; 142 if ff[q] then 143 begin 144 q:=pre[q]; 145 continue; 146 end; 147 ff[q]:=true; 148 ff[q xor 1]:=true; 149 solve(p); 150 q:=pre[q]; 151 end; 152 153 end; 154 155 156 procedure main; 157 var 158 i :longint; 159 x, y, z :longint; 160 ans1, ans2 :longint; 161 g :longint; 162 163 begin 164 read(n); 165 l:=1; 166 b[0]:=0; 167 for i:=1 to n-1 do 168 begin 169 read(x,y,z); 170 z:=z mod 3; 171 connect(x,y,z); 172 connect(y,x,z); 173 end; 174 ans:=0; 175 solve(1); 176 ans1:=2*ans+n; ans2:=n*n; 177 g:=gcd(ans1,ans2); 178 writeln(ans1 div g,'/',ans2 div g); 179 end; 180 181 begin 182 main; 183 end.
比较好的例子还有bzoj 2599
http://61.187.179.132/JudgeOnline/problem.php?id=2599
大概的意思就是给定一棵树,求路径长度为K的点对路径上点数最小是多少。
这道题与上道题的大题思路差不多,因为k给定的范围比较小为10^6,所以我们可以用一个数组来存每个长度最小用多少个节点,类似于上一题的b数组,然后也是不断地更新答案就行了。
pascal代码
1 /**************************************************************2 Problem: 25993 User: BLADEVIL4 Language: Pascal5 Result: Accepted6 Time:16008 ms7 Memory:16928 kb8 ****************************************************************/9 10 //By BLADEVIL11 var12 n, m :longint;13 pre, other, len :array[0..400010] of longint;14 last :array[0..200010] of longint;15 l, top :longint;16 size, stack, yy, ll :array[0..200010] of longint;17 ff :array[0..400010] of boolean;18 root :longint;19 b :array[0..1000001] of longint;20 old :longint;21 ans :longint;22 23 procedure connect(x,y,z:longint);24 begin25 inc(l);26 pre[l]:=last[x];27 last[x]:=l;28 other[l]:=y;29 len[l]:=z;30 end;31 32 33 procedure dfs_size(x,fa:longint);34 var35 q, p :longint;36 begin37 size[x]:=1;38 inc(top);39 stack[top]:=x;40 q:=last[x];41 while q<>0 do42 begin43 p:=other[q];44 if (ff[q]) or (p=fa) then45 begin46 q:=pre[q];47 continue;48 end;49 dfs_size(p,x);50 inc(size[x],size[p]);51 q:=pre[q];52 end;53 yy[x]:=fa;54 end;55 56 procedure getroot(u:longint);57 var58 ms, s, x, p, q :longint;59 i :longint;60 begin61 top:=0;62 dfs_size(u,0);63 ms:=maxlongint;64 for i:=1 to top do65 begin66 x:=stack[i];67 s:=size[u]-size[x];//68 q:=last[x];69 while q<>0 do70 begin71 p:=other[q];72 if (ff[q]) or (p=yy[x]) then73 begin74 q:=pre[q];75 continue;76 end;77 if size[p]>s then s:=size[p];//78 q:=pre[q];79 end;80 if s<ms then //81 begin82 ms:=s;83 root:=x;84 end;85 end;86 end;87 88 procedure dfs_value(x,fa,lz,dep:longint);89 var90 q, p :longint;91 begin92 if lz>m then exit;93 if dep>=ans then exit;94 if b[m-lz]>=0 then95 if b[m-lz]+dep<ans then ans:=b[m-lz]+dep;96 if (b[lz]<0) or (dep<b[lz]) then97 begin98 inc(top);99 stack[top]:=lz; 100 ll[top]:=dep; 101 end; 102 q:=last[x]; 103 while q<>0 do 104 begin 105 p:=other[q]; 106 if (p=fa) or (ff[q]) then 107 begin 108 q:=pre[q]; 109 continue; 110 end; 111 dfs_value(p,x,lz+len[q],dep+1); 112 q:=pre[q]; 113 end; 114 end; 115 116 procedure solve(u:longint); 117 var 118 i, q, p :longint; 119 120 begin 121 getroot(u); 122 if top=1 then exit; 123 top:=0; 124 q:=last[root]; 125 while q<>0 do 126 begin 127 p:=other[q]; 128 if ff[q] then 129 begin 130 q:=pre[q]; 131 continue; 132 end; 133 old:=top+1; 134 dfs_value(p,root,len[q],1); 135 for i:=old to top do 136 if (b[stack[i]]<0) or (b[stack[i]]>ll[i]) then b[stack[i]]:=ll[i]; 137 q:=pre[q]; 138 end; 139 140 for i:=1 to top do b[stack[i]]:=-1; 141 q:=last[root]; 142 while q<>0 do 143 begin 144 p:=other[q]; 145 if ff[q] then 146 begin 147 q:=pre[q]; 148 continue; 149 end; 150 ff[q]:=true; 151 ff[q xor 1]:=true; 152 solve(p); 153 q:=pre[q]; 154 end; 155 end; 156 157 158 procedure main; 159 var 160 i :longint; 161 x, y, z :longint; 162 163 begin 164 read(n,m); 165 l:=1; 166 fillchar(b,sizeof(b),$ff); 167 b[0]:=0; 168 for i:=1 to n-1 do 169 begin 170 read(x,y,z); 171 inc(x); inc(y); 172 connect(x,y,z); 173 connect(y,x,z); 174 end; 175 ans:=maxlongint; 176 solve(1); 177 if ans>10000000 then writeln(-1) else writeln(ans); 178 end; 179 180 begin 181 main; 182 end.
转载于:https://www.cnblogs.com/BLADEVIL/p/3510482.html
树链剖分之点剖分(点分治)讲解相关推荐
- [学习笔记]树链剖分
基本思想 树链剖分一种对树进行划分的算法,它先通过轻重边剖分将树分为多条链,保证每条边属于且只属于一条链,然后再通过数据结构来维护每一条链. 一些定义 树链:树上的路径. 剖分:把路径分类为重链和轻链 ...
- 树链剖分 or 根号分治 + dfs序 + 树状数组 ---- CF1254 D. Tree Queries
题目链接 题目大意: 问题转化: 很容易发现:假设修改的节点是vvv. 1.vvv的子树sonvson_vsonv直接加上(n−size[sonv])n×d\frac{(n-size[son_v]) ...
- 树分治树链剖分相关题目讨论
预备知识 树分治,树链剖分 poj1741 •一棵有n个节点的树,节点之间的边有长度.方方方想知道,有多少个点对距离不超过m 题解 点分治模板题.详见我早上写的http://www.cnblogs.c ...
- BZOJ4012[HNOI2015]开店——树链剖分+可持久化线段树/动态点分治+vector
题目描述 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到 人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的 想法当然非常好啦,但是她们也发现她们面临着一个 ...
- LG P3345 [ZJOI2015]幻想乡战略游戏(树的带权重心 + 树链剖分+ 动态点分治)
题目 首先这个题看起来这个最小值不好在动态点分治时维护. (居然)可以转化. 最小值于树的带权重心处取到. 带权带的是点权. 树的带权重心是子树权∗2>=总树权子树权*2>=总树权子树权∗ ...
- 【CF487E】Tourists【圆方树】【树链剖分】【multiset】
题意:给一张 nnn 点 mmm 边的连通无向图,点帯权,qqq 次操作: 修改一个点的权值. 询问两点间所有简单路的最小权值的最小值. n,m,q≤105n,m,q\leq 10^5n,m,q≤10 ...
- [ZJOI2015] 幻想乡战略游戏(树链剖分 + 线段树二分 + 带权重心)
problem luogu-P3345 solution 这是一个带权重心的题,考察动态点分治.点分治?呵,不可能的,这辈子都不可能写点分治 我们重新考虑重心的性质:以这个点为根时,所有子树的大小不会 ...
- 【ZJOI2008】树的统计(树链剖分)
传送门 Solution: 就是树链剖分入门题啦~ // luogu-judger-enable-o2 #include<bits/stdc++.h> #define N 30005 #d ...
- 树链剖分+线段树 HDOJ 4897 Little Devil I(小恶魔)
题目链接 题意: 给定一棵树,每条边有黑白两种颜色,初始都是白色,现在有三种操作: 1 u v:u到v路径(最短)上的边都取成相反的颜色 2 u v:u到v路径上相邻的边都取成相反的颜色(相邻即仅有一 ...
- P1505 [国家集训队]旅游 树链剖分
题目链接 题意:树上更新某一点权值,更新两点简单路径权值,查询最大,最小,和 思路:思路应该比较简单,就是树链剖分后用线段树维护区间最大最小以及区间和. 但是本题比较特殊的是给的边权,转化为点权即可. ...
最新文章
- 这些资源网站为什么能获得5万知乎大佬推荐,而我错失了什么吗?
- 新人python2和python3的区别_未明学院:Python2与Python3的主要区别
- C# WinForm给Button按钮或其它控件添加快捷键响应
- ubuntu 16.04 mysql5.7.17 开放远程3306端口
- idea springmvc_IDEA搭建SSM(spring+springmvc+mybatis)Maven项目的入门案例
- 转:Some interesting facts about SharePoint 2007 Search
- mac系统下为emacs设置中文字体,解决乱码问题
- d3.js 旋转图形_1.基础知识(3) Matlab绘制特殊的图形
- html5怎么给视频加字幕,怎么给视频添加文字 怎么用Premiere Pro cs6给视频中添加文字...
- Windows中MSOCache文件夹
- 如何设置正确的dns服务器地址,dns服务器地址如何设置
- perl 简明教程 perl教程集合
- jar启动出错:Failed to get nested archive for entry BOOT-INF/lib/activiti-core-common-dependencies-7.1.0
- Java和c的一些不同(一)
- java实训第二次作业//181021(个人所得税计算器;判断闰年与平年;个人作业展示系统(方法调用))
- 计算机技术离不开量子力学,高分子与计算机模拟
- 世态炎凉,冷暖自知-一名某易公司员工自述的裁员经历
- Android学习:线程同步之synchronized
- 携手吉林农信社 赛门铁克引备份新变革
- 用Python实现Excel表格的多对多批量替换
热门文章
- Vista下将Area效果应用到整个窗体
- iOS/Android React Native 配置教程
- 第三章 Goroutine调度策略(16)
- servlet,listener,filter,interceptor的关系
- 可变参数宏__VA_ARGS__和...
- centos+nginx从零开始配置负载均衡
- MSSQL备份及数据迁移
- 47页PPT,海量信息!用户画像架构、指标、标签、ETL、性能及案例应用一站通!(附PPT下载及视频 )...
- python psutil 获取命令历史_python中系统信息获取psutil使用
- 简单的Spring配置文件