dtoj#4258. 铃铛计数问题
Posted jessie-
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了dtoj#4258. 铃铛计数问题相关的知识,希望对你有一定的参考价值。
题目描述:
圣诞节来了,仓鼠又要来策划活动了,今年仓鼠会在圣诞树上挂上铃铛!
已知圣诞树有 $n$ 个节点,并且根节点是固定的。记 $s[i]$ 表示以 $i$ 为根的子树中,所有节点上铃铛数目的总和。但仓鼠觉得询问 $s[i]$ 太简单了,他决定给定 $l$ 和 $r$,要你回答 $sumlimits_{i=l}^{r}s[i]$ 的值。
但是为了避免有的人一次预处理后一劳永逸,仓鼠在大家答题的过程中还会修改某个节点上灯笼的数量。仓鼠还要去筹备活动,你能帮助他写一个程序帮助实时给出标准答案吗?
算法标签:分块
思路:
分块好题。
比较容易想到分块处理,但是这题要把各个部分分开算。
$cnt[x][i]$ 表示在第 $i$ 个块,有多少个点在 $x$ 到根的链上,那么每次修改 $val[x]$ 就会发生 $cnt[x][i] imes (new-val[x])$ 的权值变化。
对于单个点价值的计算,按照 $dfn$ 序做前缀和,单点修改区间查询,考虑再分一次块,对于每一次修改,对整个块有影响的对于这个块统一加一个值,否则单点加。那么一次查询效率就是 $O(1)$ 的了。
以下代码:
#include<bits/stdc++.h> #define il inline #define LL long long #define _(d) while(d(isdigit(ch=getchar()))) using namespace std; const int N=1e5+5,M=320; LL res[M],sum[N],tag[M],g[N]; int gr[N],sz,num[M][N],dfn[N],ed[N],tot; int n,Q,val[N],rt,head[N],ne[N<<1],to[N<<1],cnt; il int read(){ int x,f=1;char ch; _(!)ch==‘-‘?f=-1:f;x=ch^48; _()x=(x<<1)+(x<<3)+(ch^48); return f*x; } il void ins(int x,int y){ ne[++cnt]=head[x]; head[x]=cnt;to[cnt]=y; } il void dfs(int x,int fa){ for(int i=1;i<=gr[n];i++)num[i][x]=num[i][fa]; num[gr[x]][x]++;dfn[x]=++tot; sum[tot]=sum[tot-1]+val[x];g[x]=val[x]; for(int i=head[x];i;i=ne[i]){ if(fa==to[i])continue; dfs(to[i],x);g[x]+=g[to[i]]; } ed[x]=tot;res[gr[x]]+=g[x]; } il LL cal(int x){ return sum[ed[x]]-sum[dfn[x]-1]+tag[gr[ed[x]]]-tag[gr[dfn[x]-1]]; } il void change(int x,int v){ for(int i=1;i<=gr[n];i++)res[i]+=1ll*v*num[i][x]; int p=dfn[x]; for(int i=gr[p]+1;i<=gr[n];i++)tag[i]+=v; for(int i=p;i<=min(n,gr[p]*sz);i++)sum[i]+=v; } il LL query(int l,int r){ LL ans=0; if(gr[l]==gr[r]){ for(int i=l;i<=r;i++)ans+=cal(i); return ans; } for(int i=l;i<=gr[l]*sz;i++)ans+=cal(i); for(int i=(gr[r]-1)*sz+1;i<=r;i++)ans+=cal(i); for(int i=gr[l]+1;i<gr[r];i++)ans+=res[i]; return ans; } int main() { n=read();Q=read();sz=(int)sqrt(n); for(int i=1;i<=n;i++)val[i]=read(); for(int i=1;i<=n;i++)gr[i]=(i-1)/sz+1; for(int i=1;i<=n;i++){ int x=read(),y=read(); if(x)ins(x,y),ins(y,x); else rt=y; } dfs(rt,0); while(Q--){ int op=read(),l=read(),r=read(); if(op==1){ change(l,r-val[l]);val[l]=r; } else{ printf("%lld ",query(l,r)); } } return 0; }
以上是关于dtoj#4258. 铃铛计数问题的主要内容,如果未能解决你的问题,请参考以下文章