铃铛计数问题——分块

题目

【题目描述】
圣诞节来了,仓鼠又要来策划活动了,今年仓鼠会在圣诞树上挂上铃铛!
已知圣诞树有 $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 }
View Code

 

posted @ 2019-03-20 20:53  Chm_wt  阅读(363)  评论(0编辑  收藏  举报