zoukankan      html  css  js  c++  java
  • 树链剖分之点剖分(点分治)讲解

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

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

      以一个基础题为例子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.
  • 相关阅读:
    about java
    nginx+tomcat
    sed
    百度推送
    线程及更新ui线程的简单理解
    测试异常检测的Bugly使用
    轮播图带加点,带时间自动轮播加手动轮播
    自定义listView与scrollView使用
    tabLayout加viewPager的实现
    网络获取图片列表
  • 原文地址:https://www.cnblogs.com/BLADEVIL/p/3510482.html
Copyright © 2011-2022 走看看