BZOJ3626 : [LNOI2014]LCA

求[l,r]内所有点与z的lca的深度之和 = 求z所有祖先子树中在[l,r]内的点的个数之和

 

由于数据不是随机的,所以祖先个数可能很大。

 

按编号分成$\sqrt{n}$块

设ans[i][j]表示第i块内所有点与j的lca的深度之和

计算ans[i][]时,把在[l,r]内所有点的cnt设为1,其它的设为0,

然后从下往上,父亲的cnt+=孩子的cnt,

然后再从上往下,孩子的cnt+=父亲的cnt

最后ans[i][j]就等于cnt[j]

预处理$O(n\sqrt{n})$

查询时中间直接查询,两端暴力,$O(\sqrt{n}\log n)$

 

总复杂度$O(n\sqrt{n}+q\sqrt{n}\log n)$

 

然后一开始写了倍增求lca被卡常数了TAT

然后改成了用树链剖分求lca后就过了!(而且答案本身就在int范围内所以最后再取模即可)

恩以后都打树链剖分了。

 

#include<cstdio>

#define N 50010

#define M 230

int n,q,i,j,x,z,l,r,f[N],d[N],g[N],v[N],nxt[N],ed,cnt[N],heavy[N],son[N],top[N],lim,block,st[M],en[M],pos[N],ans[M][N];

inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';}

inline void swap(int&x,int&y){int z=x;x=y;y=z;}

inline void add(int x,int y){v[++ed]=y,nxt[ed]=g[x],g[x]=ed;}

void dfs1(int x){

  cnt[x]=1;d[x]=d[f[x]]+1;

  int heavy=0,max=0,i;

  for(i=g[x];i;i=nxt[i]){

    dfs1(v[i]),cnt[x]+=cnt[v[i]];

    if(cnt[v[i]]>max)max=cnt[v[i]],heavy=v[i];

  }

  if(heavy)son[x]=heavy;

}

void dfs2(int x,int t){

  top[x]=t;

  if(son[x])dfs2(son[x],t);

  for(int i=g[x];i;i=nxt[i])if(v[i]!=son[x])dfs2(v[i],v[i]);

}

inline int lca(int x,int y){

  while(top[x]!=top[y]){

    if(d[top[x]]<d[top[y]])swap(x,y);

    x=f[top[x]];

  }

  return d[x]<d[y]?d[x]:d[y];

}

void up(int x){for(int i=g[x];i;i=nxt[i])up(v[i]),cnt[x]+=cnt[v[i]];}

void down(int x,int y){ans[y][x]=cnt[x];for(int i=g[x];i;i=nxt[i])cnt[v[i]]+=cnt[x],down(v[i],y);}

inline int ask(int l,int r,int z){

  int i,t=0;

  if(pos[l]==pos[r]){

    for(i=l;i<=r;i++)t+=lca(z,i);

    return t;

  }

  for(i=l;i<=en[pos[l]];i++)t+=lca(z,i);

  for(i=st[pos[r]];i<=r;i++)t+=lca(z,i);

  for(i=pos[l]+1;i<pos[r];i++)t+=ans[i][z];

  return t;

}

int main(){

  read(n),read(q);

  for(i=2;i<=n;i++)read(f[i]),add(++f[i],i);

  dfs1(1),dfs2(1,1);

  while(lim*lim<n)lim++;

  for(i=1;i<=n;i++)pos[i]=(i-1)/lim+1;

  for(block=pos[n],i=1;i<=block;i++)en[i-1]=(st[i]=lim*(i-1)+1)-1;en[block]=n;

  for(i=1;i<=block;i++){

    for(j=1;j<st[i];j++)cnt[j]=0;

    for(j=st[i];j<=en[i];j++)cnt[j]=1;

    for(j=en[i]+1;j<=n;j++)cnt[j]=0;

    up(1);down(1,i);

  }

  while(q--)read(l),read(r),read(z),printf("%d\n",ask(l+1,r+1,z+1)%201314);

  return 0;

}

  

 

你可能感兴趣的:(ZOJ)