块状树

BZOJ4765

将树按深度分为sqrt(n)条可相互重叠的链

修改时在树上链块外在全局的分块上修改,块内打标记。O(sqrt(n))

询问时先取分快答案,然后枚举所有链,对答案的贡献为标记之和*链锁对的位置在询问中的数量

#include <cstdio>
#include <iostream>
#include <cmath>
#define ULL unsigned long long 
using namespace std;

  int maxdep[110001],dep[110001],nd[110001],next[210001],des[210001],blsize;
  int speid[110001],cnt,spe[110001],n,m,root,fa[110001],pre[501][110001],fa_spe[110001];
  ULL ori[110001],a[110001],sum[110001],toadd[110001];
  int bt[110001];

  void addedge(int x,int y){
      next[++cnt]=nd[x];des[cnt]=y;nd[x]=cnt;
  }

  void dfs1(int po){
      maxdep[po]=dep[po];
      bt[po]=1;
      for (int p=nd[po];p!=-1;p=next[p])
      if (!bt[des[p]]){
        dep[des[p]]=dep[po]+1;
        fa[des[p]]=po;
        dfs1(des[p]);
        ori[po]+=ori[des[p]];
        maxdep[po]=max(maxdep[po],maxdep[des[p]]);
    }
    
    if (maxdep[po]-dep[po]>=blsize){
      speid[po]=++cnt;
      spe[po]=1;maxdep[po]=dep[po];    
    }
  }

  int main(){
      scanf("%d%d",&n,&m);
      for (int i=1;i<=n;i++) scanf("%llu",&ori[i]),a[i]=ori[i],nd[i]=-1;
      for (int i=1;i<=n;i++){
        int t1,t2;
        scanf("%d%d",&t1,&t2);
        if (t1==0) root=t2;else
        if (t2==0) root=t1;else
        addedge(t1,t2),addedge(t2,t1);
    }
    
    blsize=sqrt(n);
    dep[root]=1;
    cnt=0;
    dfs1(root);
    
    spe[0]=1;
    for (int i=1;i<=n;i++)
      if (spe[i]){
        int po=i;pre[speid[i]][po]=1;po=fa[po];
        while (!spe[po]){
          pre[speid[i]][po]++;
          po=fa[po];    
        }
        for (int j=1;j<=n;j++)
          pre[speid[i]][j]+=pre[speid[i]][j-1];
        fa_spe[i]=po;    
      }
      
    for (int i=1;i<=n;i++) sum[i/blsize+1]+=ori[i];
    
    int quecnt=0;
    for (int i=1;i<=m;i++){
      ULL t1,t2,t3;
      scanf("%llu%llu%llu",&t1,&t2,&t3);
      if (t1==2){
          quecnt++;
          ULL ans=0;
          for (int i=1;i<=cnt;i++) ans+=toadd[i]*(ULL)(pre[i][t3]-pre[i][t2-1]);
          while (t2%blsize&&t2<=t3) ans+=ori[t2],t2++;
          while (t3%blsize&&t3>=t2) ans+=ori[t3],t3--;
        if (t3>=t2) ans+=ori[t3--];
          if (t3>=t2) for (int i=t2/blsize+1;i<=t3/blsize+1;i++) ans+=sum[i];
          printf("%llu\n",ans);
      }else{
          ULL su=t3-a[t2];a[t2]=t3;
          int po=t2;
          while (!spe[po]){
            ori[po]+=su;
          sum[po/blsize+1]+=su;
          po=fa[po];    
        }
        while (po){
          toadd[speid[po]]+=su;
          po=fa_spe[po];
        }
      }    
    }
  }

 

posted @ 2017-03-10 10:20  z1j1n1  阅读(590)  评论(0编辑  收藏  举报