QTREE——树的路径剖分(又称树链剖分)

原题地址
【有关树的路径剖分的东东在网上介绍的太多了……】
常见的路径剖分的方法是轻重边剖分,即把树中的边分为轻重两部分,方法:设SZ[i]为以i为根的子树的大小(结点总数),则若点x不是叶结点,则其子结点中SZ值最大的(注意,有多个SZ值最大的子结点应任选一个, 只能选一个,防止出现重链相交,引发歧义)点y,边(x, y)称为重边,其余的边都是轻边。首尾相连的重边称为重链(注意一定是自上而下的),则一个很显然的性质是:从根结点到任意点i路径上的轻边与重链的总数都不会超过O(log 2N)。然后,对每条重链上的边建立线段树,每当遇到改值操作,若是轻边就直接改,若是重边就在线段树里改;遇到找x、y路径上边权最大值的操作,只要找到LCA(x, y),然后从x、y开始沿着树边上溯到LCA(x, y)处,对于中间的每条轻边和重链(线段树内)导出最大值即可。

求LCA:可以对整棵树作深度优先遍历,记下每个遇到的点(包括向上的和向下的)的编号,形成一个长度为2(N-1)的序列A,然后,找到点x、y在A中的第一次出现的位置(设为FF[x]和FF[y]),则A[FF[x]..FF[y]]中的深度最小的点的编号就是LCA(x, y),显然这需要RMQ。

具体步骤:
(1)输入部分:建立无根树;
(2)预处理部分:分为6步:
<1>利用BFS将无根树转化为有根树,同时求出有根树中点i(根结点除外)到其父结点的边的编号(设为FA[i])以及点i的深度(设为DEP[i]);
<2>自底向上计算每个点的SZ值,同时划分轻重边(对于有根树中的每条边设立Z域,Z=1为重边,Z=0为轻边);
<3>求出重链,建立线段树:
求重链的方法:由于重链只会在叶结点处结束,因此从每个叶结点开始上溯,直到上溯到根或者遇到轻边为止。为了方便,需要对每个结点i记录以下4个值:UP[i]表示点i所在重链最顶上的那个结点的编号;ord[i]表示点i是其所在重链的上起第几个点(从0开始);tot[i]表示点i所在重链上有几个结点;root[i]表示点i所在重链建成的线段树的编号(这样经常写的opr(0, n-1, root)就可以表示成opr(0, tot[i]-1, root[i]))。求出重链以后,对沿途经历的所有边的权值 倒序写入数组W0,再对数组W0建线段树即可。考虑到不同线段树的大小可能不同,这里采用压缩处理,这样就需要记录每个点的lch、rch编号(见代码);
<4>对树进行DFS遍历,求出序列A;
<5>求出序列A的倍增最小值,存放在A0[][]里(注意:A和A0中记载的都是编号,而判定大小的关键字是深度);
<6>求出LOG[i]=log 2i(下取整);
(3)执行部分:对于询问操作,求LCA,不断上溯,对于重链在线段树里找即可,注意线段树的左右端点,尤其是当UP在LCA之上时,只能上溯到LCA处。

编程注意事项:建代码中标Attention的地方。
代码(我这个代码常数巨大,时间达3.61s,不知是肿么搞得,神犇来看一下啊囧):
#include  < iostream >
#include 
< stdio.h >
#include 
< stdlib.h >
#include 
< string .h >
using   namespace  std;
#define  re(i, n) for (int i=0; i<n; i++)
#define  re1(i, n) for (int i=1; i<=n; i++)
#define  re2(i, l, r) for (int i=l; i<r; i++)
#define  re3(i, l, r) for (int i=l; i<=r; i++)
#define  rre(i, n) for (int i=n-1; i>=0; i--)
#define  rre1(i, n) for (int i=n; i>0; i--)
#define  rre2(i, r, l) for (int i=r-1; i>=l; i--)
#define  rre3(i, r, l) for (int i=r; i>=l; i--)
const   int  MAXN  =   10001 , MAXS  =   20 , INF  =   ~ 0U   >>   2 ;
struct  edge {
    
int  a, b, w, pre, next;
    
bool  Z;
} E0[MAXN 
<<   2 ], E[MAXN  +  MAXN  +   1 ];
struct  node {
    
int  maxv, lch, rch;
} T[MAXN 
<<   2 ];
int  n, m0, m, N, _a[MAXN], _b[MAXN], FA[MAXN], Q[MAXN], SZ[MAXN], DEP[MAXN], W0[MAXN], UP[MAXN], ord[MAXN], root[MAXN], tot[MAXN];
int  n1, stk[MAXN], st[MAXN], A[MAXN  <<   2 ], A0[MAXN  <<   2 ][MAXS], FF[MAXN], LOG[MAXN  <<   2 ], l0, r0, x0, res;
bool  vst[MAXN];
void  init_d()
{
    re(i, n) E0[i].pre 
=  E0[i].next  =  E[i].pre  =  E[i].next  =  i;
    m0 
=  m  =  n;
}
void  add_edge0( int  a,  int  b,  int  w)
{
    E0[m0].a 
=  a; E0[m0].b  =  b; E0[m0].w  =  w; E0[m0].pre  =  E0[a].pre; E0[m0].next  =  a; E0[a].pre  =  m0; E0[E0[m0].pre].next  =  m0 ++ ;
    E0[m0].a 
=  b; E0[m0].b  =  a; E0[m0].w  =  w; E0[m0].pre  =  E0[b].pre; E0[m0].next  =  b; E0[b].pre  =  m0; E0[E0[m0].pre].next  =  m0 ++ ;
}
void  add_edge( int  a,  int  b,  int  w)
{
    E[m].a 
=  a; E[m].b  =  b; E[m].w  =  w; E[m].Z  =   0 ; E[m].pre  =  E[a].pre; E[m].next  =  a; E[a].pre  =  m; E[E[m].pre].next  =  m ++ ;
}
int  mkt( int  l,  int  r)
{
    
int  No  =   ++ N;
    
if  (l  ==  r) {
        T[No].maxv 
=  W0[l]; T[No].lch  =  T[No].rch  =   0 ;
    } 
else  {
        
int  mid  =  l  +  r  >>   1 , l_r  =  mkt(l, mid), r_r  =  mkt(mid  +   1 , r); T[No].lch  =  l_r; T[No].rch  =  r_r;
        
if  (T[l_r].maxv  >=  T[r_r].maxv) T[No].maxv  =  T[l_r].maxv;  else  T[No].maxv  =  T[r_r].maxv;
    }
    
return  No;
}
void  prepare()
{
    re(i, n) vst[i] 
=   0 ; Q[ 0 =   0 ; vst[ 0 =   1 ; DEP[ 0 =   0 ; FA[ 0 =   - 1 ;
    
int  i0, j0;
    
for  ( int  front = 0 , rear = 0 ; front <= rear; front ++ ) {
        i0 
=  Q[front];
        
for  ( int  p = E0[i0].next; p  !=  i0; p = E0[p].next) {
            j0 
=  E0[p].b;
            
if  ( ! vst[j0]) {add_edge(i0, j0, E0[p].w); vst[j0]  =   1 ; Q[ ++ rear]  =  j0; FA[j0]  =  m  -   1 ; DEP[j0]  =  DEP[i0]  +   1 ;}
        }
    }
    
int  maxSZ, x, n0, root0;
    rre(i, n) {
        i0 
=  Q[i]; SZ[i0]  =   1 ; maxSZ  =   0 ;
        
for  ( int  p = E[i0].next; p  !=  i0; p = E[p].next) {SZ[i0]  +=  SZ[j0  =  E[p].b];  if  (SZ[j0]  >  maxSZ) {maxSZ  =  SZ[j0]; x  =  p;}}
        
if  (SZ[i0]  >   1 ) E[x].Z  =   1 ;    // Attention
    }
    UP[
0 =   0 ; ord[ 0 =   0 ; N  =   0 ;
    re2(i, 
1 , n) {
        i0 
=  Q[i]; x  =  FA[i0];  if  (E[x].Z) {UP[i0]  =  UP[E[x].a]; ord[i0]  =  ord[E[x].a]  +   1 ;}  else  {UP[i0]  =  i0; ord[i0]  =   0 ;}
        
if  (SZ[i0]  ==   1   &&  ord[i0]) {
            j0 
=  UP[i0]; n0  =  ord[i0];
            
for  ( int  j = i0; j != j0; j = E[FA[j]].a) {tot[j]  =  ord[i0]; W0[ -- n0]  =  E[FA[j]].w;} tot[j0]  =  ord[i0];   // Attention
            root0  =  mkt( 0 , ord[i0]  -   1 );   // Attention
             for  ( int  j = i0; j != j0; j = E[FA[j]].a) root[j]  =  root0; root[j0]  =  root0;   // Attention
        }
    }
    re(i, n) {st[i] 
=  E[i].next; FF[i]  =   - 1 ;} stk[ 0 =   0 int  tp  =   0 ; n1  =   1 ; A[ 0 =  FF[ 0 =   0 ;
    
while  (tp  >=   0 ) {
        i0 
=  stk[tp]; x  =  st[i0];
        
if  (x  !=  i0) {
            j0 
=  E[x].b;  if  (FF[j0]  ==   - 1 ) FF[j0]  =  n1; A[n1 ++ =  j0; st[i0]  =  E[x].next; stk[ ++ tp]  =  j0;
        } 
else  {
            
if  (tp) A[n1 ++ =  stk[tp  -   1 ];
            tp
-- ;
        }
    }
    rre(i, n1) {
        A0[i][
0 =  A[i]; x  =   1 ;
        re2(j, 
1 , MAXS)  if  (i  +  (x  <<   1 <=  n1) {
            
if  (DEP[A0[i][j  -   1 ]]  <=  DEP[A0[i  +  x][j  -   1 ]]) A0[i][j]  =  A0[i][j  -   1 ];  else  A0[i][j]  =  A0[i  +  x][j  -   1 ];   // Attention
            x  <<=   1 ;
        } 
else   break ;
    }
    
int  _x; x  =   1 ;
    re(i, MAXS) {
        _x 
=  x  <<   1 ;
        
if  (_x  <  n1) re2(j, x, _x) LOG[j]  =  i;  else  {re2(j, x, n1) LOG[j]  =  i;  break ;}
        x 
=  _x;
    }
}
void  opr0( int  l,  int  r,  int  No)
{
    
if  (l  ==  l0  &&  r  ==  l0) T[No].maxv  =  x0;  else  {
        
int  mid  =  l  +  r  >>   1 , lch  =  T[No].lch, rch  =  T[No].rch;
        
if  (mid  >=  l0) opr0(l, mid, lch);
        
if  (mid  <  l0) opr0(mid  +   1 , r, rch);
        
if  (T[lch].maxv  >=  T[rch].maxv) T[No].maxv  =  T[lch].maxv;  else  T[No].maxv  =  T[rch].maxv;
    }
}
void  opr1( int  l,  int  r,  int  No)
{
    
if  (l  >=  l0  &&  r  <=  r0) {
        
if  (T[No].maxv  >  res) res  =  T[No].maxv;
    } 
else  {
        
int  mid  =  l  +  r  >>   1 ;
        
if  (mid  >=  l0) opr1(l, mid, T[No].lch);
        
if  (mid  <  r0) opr1(mid  +   1 , r, T[No].rch);
    }
}
int  main()
{
    
int  tests, a0, b0, w0, LCA, LOG0, FF0, FF1, p0, tmp;
    
char  ss[ 20 ], ch;
    scanf(
" %d " & tests);
    re(testno, tests) {
        scanf(
" %d " & n); init_d();
        re(i, n
- 1 ) {scanf( " %d%d%d " & a0,  & b0,  & w0); add_edge0( -- a0,  -- b0, w0); _a[i]  =  a0; _b[i]  =  b0;}
        prepare(); ch 
=  getchar();
        
while  ( 1 ) {
            scanf(
" %s " , ss);
            
if  ( ! strcmp(ss,  " QUERY " )) {
                scanf(
" %d%d%*c " & a0,  & b0);  -- a0;  -- b0;
                
if  (a0  ==  b0) res  =   0 else  res  =   - INF;
                FF0 
=  FF[a0]; FF1  =  FF[b0];
                
if  (FF0  >  FF1) {tmp  =  FF0; FF0  =  FF1; FF1  =  tmp;}
                LOG0 
=  LOG[FF1  -  FF0  +   1 ];
                
if  (DEP[A0[FF0][LOG0]]  <=  DEP[A0[FF1  -  ( 1   <<  LOG0)  +   1 ][LOG0]]) LCA  =  A0[FF0][LOG0];  else  LCA  =  A0[FF1  -  ( 1   <<  LOG0)  +   1 ][LOG0];
                
while  (a0  !=  LCA) {
                    p0 
=  FA[a0];
                    
if  (E[p0].Z) {
                        r0 
=  ord[a0]  -   1 if  (DEP[UP[a0]]  >=  DEP[LCA]) l0  =   0 else  l0  =  ord[LCA];   // Attention
                         if  (l0  <=  r0) opr1( 0 , tot[a0]  -   1 , root[a0]);   // Attention
                         if  (l0) a0  =  LCA;  else  a0  =  UP[a0];
                    } 
else  {
                        
if  (E[p0].w  >  res) res  =  E[p0].w;
                        a0 
=  E[p0].a;
                    }
                }
                
while  (b0  !=  LCA) {
                    p0 
=  FA[b0];
                    
if  (E[p0].Z) {
                        r0 
=  ord[b0]  -   1 if  (DEP[UP[b0]]  >=  DEP[LCA]) l0  =   0 else  l0  =  ord[LCA];   // Attention
                         if  (l0  <=  r0) opr1( 0 , tot[b0]  -   1 , root[b0]);   // Attention
                         if  (l0) b0  =  LCA;  else  b0  =  UP[b0];
                    } 
else  {
                        
if  (E[p0].w  >  res) res  =  E[p0].w;
                        b0 
=  E[p0].a;
                    }
                }
                printf(
" %d\n " , res);
            } 
else   if  ( ! strcmp(ss,  " CHANGE " )) {
                scanf(
" %d%d " & a0,  & w0); b0  =  _b[ -- a0]; a0  =  _a[a0];
                
if  (FA[b0]  ==   - 1   ||  E[FA[b0]].a  !=  a0) {tmp  =  a0; a0  =  b0; b0  =  tmp;}
                p0 
=  FA[b0];
                
if  (E[p0].Z) {
                    l0 
=  ord[a0]; x0  =  w0; opr0( 0 , tot[a0]  -   1 , root[a0]);
                } 
else  E[p0].w  =  w0;
            } 
else   break ;
        }
    }
    
return   0 ;
}
树的路径剖分是解决树上路径的操作查询问题的有力工具,它还有一些更为强大的应用,以后再来搞……

你可能感兴趣的:(QTREE——树的路径剖分(又称树链剖分))