树链剖分

树链剖分就是把一个树分成了几条链。

轻重链剖分:对于一个结点,我们把它所有的儿子结点中子树结点数最大的那个成为重儿子,这样每个结点朝重儿子连边,称为重边,其他边称为轻边,最后重边就构成了若干链。

这里写图片描述

对于重链,我们用线段树维护信息,查询一条链把它从lca分为两条链,自底向上查询,轻边直接搞,重边在线段树里搞,每次经过轻边节点数加倍,复杂度为\(O(log^2n)\)

操作:

  1. DFS第一遍,确定子树节点数和重儿子下标
  2. DFS第二遍,确定DFS序,当前重链顶点,LCA所需信息
  3. 建出线段树
  4. 预处理LCA

代码:

struct Edge{
 int v,val,next;
} E[maxn*2];

int type,n,m,x,y,size,Esize,val,lca,pos;
int last[maxn],F[maxn][maxlogn],sum[maxn],hs[maxn],A[maxn],vs[maxn],top[maxn],ind[maxn];
int depth[maxn],hv[maxn],T[maxn*3],v[maxn],ex[maxn],ey[maxn];

void addedge(int x,int y,int val)
{
 E[++Esize]=(Edge){y,val,last[x]},last[x]=Esize;
 E[++Esize]=(Edge){x,val,last[y]},last[y]=Esize;
}

//---------------------------------------

void pre_trans(int k,int fa)
{
 sum[k]=1,hs[k]=0; //子树结点和,重儿子
 for (int i=last[k];i;i=E[i].next)
  if (E[i].v!=fa)
  {
   pre_trans(E[i].v,k),sum[k]+=sum[E[i].v];
   if (sum[E[i].v]>sum[hs[k]]) hs[k]=E[i].v,hv[k]=E[i].val;
  }
}

void trans(int k,int fa,int t,int d,int val)
{
 if (k!=t) vs[++size]=k,ind[k]=size; //在线段树的下标
 F[k][0]=fa,top[k]=t,depth[k]=d,A[k]=val; //父亲,重链顶点
 
 if (hs[k]) trans(hs[k],k,t,d+1,hv[k]);
 for (int i=last[k];i;i=E[i].next)
  if ((E[i].v!=fa)&&(E[i].v!=hs[k])) trans(E[i].v,k,E[i].v,d+1,E[i].val);
}

//---------------------------------------

int Build_Tree(int k,int l,int r)
{
 if (l==r)
  T[k]=A[vs[l]];
 else
 {
  int mid=(l+r)>>1;
  T[k]=Build_Tree((k<<1),l,mid)+Build_Tree((k<<1)|1,mid+1,r); 
 }
 return T[k];
}

void add(int k,int l,int r,int x,int val)
{
 if (l==r)
  T[k]=val;
 else
 {
  int mid=(l+r)>>1;
  if (x<=mid) add((k<<1),l,mid,x,val); else add((k<<1)|1,mid+1,r,x,val);
  T[k]=T[k<<1]+T[(k<<1)|1];
 }
}

int min_query(int k,int l,int r,int x,int y)
{
 if ((x<=l)&&(r<=y))
  return T[k];
 else
 {
  int mid=(l+r)>>1,ans=0;
  if (x<=mid) ans+=min_query((k<<1),l,mid,x,y);
  if (y>mid) ans+=min_query((k<<1)|1,mid+1,r,x,y);
  return ans;
 }
}

//---------------------------------------

void LCA_init()
{
 rep(k,1,maxlogn-1)
  rep(i,1,n) F[i][k]=(F[i][k-1]==-1)?(-1):(F[F[i][k-1]][k-1]);
}

int LCA_query(int x,int y)
{
 if (depth[x]>depth[y]) swap(x,y);
 rep(k,0,maxlogn-1) if (((depth[y]-depth[x])>>k)&1) y=F[y][k];

 if (x==y) return x;
 dep(k,maxlogn-1,0) if (F[x][k]!=F[y][k]) x=F[x][k],y=F[y][k];
 return F[x][0];
}

//---------------------------------------

void heavylight()
{
 pre_trans(1,-1);
 trans(1,-1,1,1,0);

 Build_Tree(1,1,size);
 LCA_init();
}

void update(int x,int y,int val)
{
 if (F[x][0]==y) swap(x,y);
 if (!ind[y]) A[y]=val; else add(1,1,size,ind[y],val);
}

int ans_query(int k,int t) //查询sum
{
 int ans=0,tf,mark=0;
 while (!mark)
  if (!ind[k])
   ans+=A[k],k=F[k][0]; //轻边直接加
  else
  {
   if (top[t]==top[k]) {tf=hs[t];mark=1;} else tf=hs[top[k]];
   ans+=min_query(1,1,size,ind[tf],ind[k]); //重边用线段树
   k=top[k];
  }
 
 return ans;
}


posted @ 2017-03-12 20:51  Krew  阅读(130)  评论(0)    收藏  举报