块状树
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]; } } } }