树链剖分之点剖分(点分治)讲解
当一个问题可以分解成小问题时,我们一般可以采用分治算法,比如最简单的快速排序,就是分治算法的一个典型的应用。
那么处理树的问题时,假设求解满足条件的点对儿个数,对于一个树来说,两个点对儿的存在只能有两种情况,就是点对之间的路径过根和不过根,那么对于不过根的情况递归做,对于一棵树只考虑过根的情况,计算就行了。
以一个基础题为例子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); var ms, s, x, p, q :longint; i :longint; begin top:=0; dfs_size(u,0);//这个操作是处理出子树的size值 ms:=maxlongint; for i:=1 to top do begin x:=stack[i]; s:=size[u]-size[x]; q:=last[x]; while q<>0 do begin p:=other[q]; if (ff[q]) or (p=yy[x]) then begin q:=pre[q]; continue; end; if size[p]>s then s:=size[p]; q:=pre[q]; end; if s<ms then begin ms:=s; root:=x; end; end; end;
这道题的pascal代码
1 /************************************************************** 2 Problem: 2152 3 User: BLADEVIL 4 Language: Pascal 5 Result: Accepted 6 Time:564 ms 7 Memory:1360 kb 8 ****************************************************************/ 9 10 //By BLADEVIL 11 var 12 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 begin 24 if x<y then exit(gcd(y,x)) else 25 if y=0 then exit(x) else exit(gcd(y,x mod y)); 26 end; 27 28 procedure connect(x,y,z:longint); 29 begin 30 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 var 39 q, p :longint; 40 begin 41 size[x]:=1; 42 inc(top); 43 stack[top]:=x; 44 q:=last[x]; 45 while q<>0 do 46 begin 47 p:=other[q]; 48 if (ff[q]) or (p=fa) then 49 begin 50 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 var 62 ms, s, x, p, q :longint; 63 i :longint; 64 begin 65 top:=0; 66 dfs_size(u,0); 67 ms:=maxlongint; 68 for i:=1 to top do 69 begin 70 x:=stack[i]; 71 s:=size[u]-size[x]; 72 q:=last[x]; 73 while q<>0 do 74 begin 75 p:=other[q]; 76 if (ff[q]) or (p=yy[x]) then 77 begin 78 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 then 85 begin 86 ms:=s; 87 root:=x; 88 end; 89 end; 90 end; 91 92 procedure dfs_value(x,fa,lz:longint); 93 var 94 q, p :longint; 95 begin 96 inc(sum[lz mod 3]); 97 q:=last[x]; 98 while q<>0 do 99 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: 2599 3 User: BLADEVIL 4 Language: Pascal 5 Result: Accepted 6 Time:16008 ms 7 Memory:16928 kb 8 ****************************************************************/ 9 10 //By BLADEVIL 11 var 12 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 begin 25 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 var 35 q, p :longint; 36 begin 37 size[x]:=1; 38 inc(top); 39 stack[top]:=x; 40 q:=last[x]; 41 while q<>0 do 42 begin 43 p:=other[q]; 44 if (ff[q]) or (p=fa) then 45 begin 46 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 var 58 ms, s, x, p, q :longint; 59 i :longint; 60 begin 61 top:=0; 62 dfs_size(u,0); 63 ms:=maxlongint; 64 for i:=1 to top do 65 begin 66 x:=stack[i]; 67 s:=size[u]-size[x];// 68 q:=last[x]; 69 while q<>0 do 70 begin 71 p:=other[q]; 72 if (ff[q]) or (p=yy[x]) then 73 begin 74 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 begin 82 ms:=s; 83 root:=x; 84 end; 85 end; 86 end; 87 88 procedure dfs_value(x,fa,lz,dep:longint); 89 var 90 q, p :longint; 91 begin 92 if lz>m then exit; 93 if dep>=ans then exit; 94 if b[m-lz]>=0 then 95 if b[m-lz]+dep<ans then ans:=b[m-lz]+dep; 96 if (b[lz]<0) or (dep<b[lz]) then 97 begin 98 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.