铃铛计数问题——分块
题目
【题目描述】
圣诞节来了,仓鼠又要来策划活动了,今年仓鼠会在圣诞树上挂上铃铛!
已知圣诞树有 $n$ 个节点,并且根节点是固定的。记 $s[i]$ 表示以 $i$ 为根的子树中,所有节点上铃铛数目的总和。但仓鼠觉得询问 $s[i]$ 太简单了,他决定给定 $l$ 和 $r$,要你回答 $\sum\limits_{i=l}^{r}s[i]$ 的值。
但是为了避免有的人一次预处理后一劳永逸,仓鼠在大家答题的过程中还会修改某个节点上灯笼的数量。仓鼠还要去筹备活动,你能帮助他写一个程序帮助实时给出标准答案吗?
【输入格式】
第一行为两个整数 $n$ 和 $q$,分别表示圣诞树的节点数和仓鼠操作的次数。
第二行 $n$ 个正整数,第 $n$ 个数 $w[i]$ 表示初始状态下第 $i$ 号节点的铃铛数。
接下来 $n$ 行,第 $i$ 行两个正整数 $u[i]$ 和 $v[i]$ ,描述一条树上的边,特别地,$u=0$ 时,表示 $v[i]$ 为圣诞树的根节点。你需要特别注意的是 $u[i]$ 不一定是 $v[i]$ 的父亲节点,也有可能 $v[i]$ 是 $u[i]$ 的父亲节点。
接下来 $q$ 行,每行三个正整数$op,l,r$。描述 $q$ 组操作。当 $op=1$ 时表示将编号为 $l$ 的节点上的铃铛修改为 $r$;当 $op=2$ 时表示询问 $\sum
\limits_{i=l}^{r}s[i]$ 的值。
【输出格式】
对于每组询问操作,你需要依据当前圣诞树情况输出该组询问的标准答案,每次询问的答案独占一行。
【样例输入】
6 6
7 4 3 4 9 1
4 2
0 1
2 1
2 3
5 3
6 5
2 1 3
1 1 1
2 3 6
2 3 5
1 3 5
2 6 6
【样例输出】
62
28
27
1
【数据范围与提示】
对于 $25\%$ 的数据, $n,q \le 300$;
对于 $40\%$ 的数据, $n,q \le 3000$;
对于另外 $10\%$ 的数据,所有操作 $op=2$;
对于另外 $15\%$ 的数据,所有 $op=2$ 的操作 $l=r$;
对于 $100\%$ 的数据,$n,q \le 100000$ ,每个结点的铃铛数始终不超过 $10^9$。
题解
考虑分块
对原编号分块
记 $ f(i,j) $ 表示 $ i $ 这个点对块 $ j $ 的贡献,显然是可以由 $fa[i]$ 转移过来
记 $ res[i] $ 表示 $ i $ 这个块内的子树大小和,那么修改点 $ x $ 为 $ v $ 对块 $ i $ 的贡献为 $ f[x][i]\times v $
那么查询 $[l,r]$ 时对于完整的快直接加上这个快的 $res[x]$,对于左右两个不完整的块,查询单点贡献即可
接下来就是如何查询单点的贡献
对这颗树进行 dfs 时,记下这个点的 dfs 序和子树范围
记 $sum[i]=\sum_{k=1}^i val[k],tag[i] $ 表示 $i$ 这个块前缀和被加的的值
对单点进行修改时,$i$ 后的块的 $tag$ 直接加上 $v$,暴力修改 $i$ 对这个块内的 $sum$ 的影响
查询单点的答案即为 $sum[ed[i]]-sum[dfn[i]-1]+tag[a[ed[i]]]-tag[a[dfn[i]-1]]$
代码
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define cal(i) (sum[ed[i]]-sum[dfn[i]-1]+tag[a[ed[i]]]-tag[a[dfn[i]-1]]) 4 #define _(d) while(d(isdigit(ch=getchar()))) 5 using namespace std; 6 int R(){ 7 int x;bool f=1;char ch;_(!)if(ch=='-')f=0;x=ch^48; 8 _()x=(x<<3)+(x<<1)+(ch^48);return f?x:-x;} 9 const int N=1e5+5,M=325; 10 int n,m,f[N][M],head[N],cnt,Rt,Sz,a[N],val[N],Mx,tot,dfn[N],ed[N]; 11 LL sum[N],res[N],sz[N],tag[N]; 12 struct edge{int to,nex;}e[N<<1]; 13 void add(int s,int t){e[++cnt]=(edge){t,head[s]},head[s]=cnt;} 14 void dfs(int u,int fa){ 15 for(int i=1;i<=Mx;i++)f[u][i]=f[fa][i]; 16 f[u][a[u]]++,dfn[u]=++tot; 17 sum[tot]=sum[tot-1]+val[u],sz[u]=val[u]; 18 for(int k=head[u],v;k;k=e[k].nex) 19 if((v=e[k].to)!=fa) 20 dfs(v,u),sz[u]+=sz[v]; 21 ed[u]=tot,res[a[u]]+=sz[u]; 22 return; 23 } 24 void change(int x,int v){ 25 for(int i=1;i<=Mx;i++)res[i]+=1ll*v*f[x][i]; 26 int p=dfn[x]; 27 for(int i=p;i<=min(a[p]*Sz,n);i++)sum[i]+=v; 28 for(int i=a[p]+1;i<=Mx;i++)tag[i]+=v; 29 return; 30 } 31 LL ask(int l,int r){ 32 LL ans=0; 33 if(a[l]==a[r]){ 34 for(int i=l;i<=r;i++)ans+=cal(i); 35 return ans; 36 } 37 for(int i=l;i<=min(a[l]*Sz,n);i++)ans+=cal(i); 38 for(int i=(a[r]-1)*Sz+1;i<=r;i++)ans+=cal(i); 39 for(int i=a[l]+1;i<a[r];i++)ans+=res[i]; 40 return ans; 41 } 42 int main(){ 43 n=R(),m=R(),Sz=sqrt(n); 44 for(int i=1;i<=n;i++)val[i]=R(),a[i]=(i-1)/Sz+1; 45 Mx=a[n]; 46 for(int i=1;i<=n;i++){ 47 int u=R(),v=R(); 48 if(!u)Rt=v; 49 else add(u,v),add(v,u); 50 } 51 dfs(Rt,0); 52 for(int i=1;i<=m;i++){ 53 int op=R(),l=R(),r=R(); 54 if(op==1) 55 change(l,r-val[l]),val[l]=r; 56 if(op==2) 57 printf("%lld\n",ask(l,r)); 58 } 59 return 0; 60 }