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;
}

  

 

posted @ 2014-07-09 20:45  Claris  阅读(438)  评论(0编辑  收藏  举报