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

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

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

  以一个基础题为例子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.

 

 

你可能感兴趣的:(树)