树链剖分之点剖分(点分治)讲解

  当一个问题可以分解成小问题时,我们一般可以采用分治算法,比如最简单的快速排序,就是分治算法的一个典型的应用。

  那么处理树的问题时,假设求解满足条件的点对儿个数,对于一个树来说,两个点对儿的存在只能有两种情况,就是点对之间的路径过根和不过根,那么对于不过根的情况递归做,对于一棵树只考虑过根的情况,计算就行了。

  以一个基础题为例子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.
原文地址:https://www.cnblogs.com/BLADEVIL/p/3510482.html