4765: 普通计算姬
Description
“奋战三星期,造台计算机”。小
G
G
G响应号召,花了三小时造了台普通计算姬。普通计算姬比普通计算机要厉害一些
。普通计算机能计算数列区间和,而普通计算姬能计算树中子树和。更具体地,小
G
G
G的计算姬可以解决这么个问题
:给定一棵
n
n
n个节点的带权树,节点编号为
1
1
1到
n
n
n,以
r
o
o
t
root
root为根,设
s
u
m
[
p
]
sum[p]
sum[p]表示以点
p
p
p为根的这棵子树中所有节点的权
值和。计算姬支持下列两种操作:
1
1
1 给定两个整数
u
,
v
u,v
u,v,修改点
u
u
u的权值为
v
v
v。
2
2
2 给定两个整数
l
,
r
l,r
l,r,计算
s
u
m
[
l
]
+
s
u
m
[
l
+
1
]
+
.
.
.
.
+
s
u
m
[
r
−
1
]
+
s
u
m
[
r
]
sum[l]+sum[l+1]+....+sum[r-1]+sum[r]
sum[l]+sum[l+1]+....+sum[r−1]+sum[r]
尽管计算姬可以很快完成这个问题,可是小G并不知道它的答案是否正确,你能帮助他吗?
Input
第一行两个整数
n
,
m
n,m
n,m,表示树的节点数与操作次数。
接下来一行
n
n
n个整数,第i个整数
d
i
d_i
di表示点
i
i
i的初始权值。
接下来
n
n
n行每行两个整数
a
i
,
b
i
a_i,b_i
ai,bi,表示一条树上的边,若
a
i
=
0
a_i=0
ai=0则说明
b
i
b_i
bi是根。
接下来
m
m
m行每行三个整数,第一个整数
o
p
op
op表示操作类型。
若
o
p
=
1
op=1
op=1则接下来两个整数
u
,
v
u,v
u,v表示将点u的权值修改为
v
v
v。
若
o
p
=
2
op=2
op=2则接下来两个整数
l
,
r
l,r
l,r表示询问。
N
≤
1
0
5
,
M
<
=
1
0
5
N\leq 10^5,M<=10^5
N≤105,M<=105
0
≤
d
i
,
V
<
2
31
,
1
≤
L
≤
R
≤
N
,
1
≤
U
≤
N
0\leq d_i,V<2^{31},1\leq L\leq R\leq N,1\leq U\leq N
0≤di,V<231,1≤L≤R≤N,1≤U≤N
Output
对每个操作类型2输出一行一个整数表示答案。
Sample Input
6 4
0 0 3 4 0 1
0 1
1 2
2 3
2 4
3 5
5 6
2 1 2
1 1 1
2 3 6
2 3 5
Sample Output
16
10
9
题意
- 就是给你一颗带有点权的树,然后需要支持两种操作:
1 u v 1\ u\ v 1 u v:修改节点u的权值为v
2 l r 2\ l\ r 2 l r:查询 ∑ i = l r 以 节 点 i 为 根 的 子 树 的 权 值 和 \sum_{i=l}^{r}{以节点i为根的子树的权值和} ∑i=lr以节点i为根的子树的权值和
题解
- 显然暴力做法就是建 d f s dfs dfs序,然后用树状数组维护一下前缀和,这样更新复杂度为 O ( log n ) O(\log n) O(logn),查询复杂度 O ( n log n ) O(n\log n) O(nlogn),考虑均摊一下这个复杂度,即将所有节点分个块,然后考虑对于任意节点 i i i对每一个块的影响(即影响的节点个数),这个可以用一遍 d f s dfs dfs跑出来,然后通过维护块内所有节点的子树权值和,可以做到整块 O ( 1 ) O(1) O(1)查询,然后非整块可以直接用树状数组维护 d f s dfs dfs序的权值前缀和,然后暴力算出非整块的每一个节点的子树权值和,这样复杂度为 O ( n n log n ) O(n \sqrt n \log n) O(nnlogn)
- 注意会爆 l o n g l o n g long\ long long long
代码
/**************************************************************
Problem: 4765
User: wzw1105
Language: C++
Result: Accepted
Time:11568 ms
Memory:168720 kb
****************************************************************/
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
vector<int>vec[maxn];
int a[maxn],root,tim=0,in[maxn],out[maxn],relate[maxn][400],n,q,sta[400];
namespace bit{
unsigned long long s[maxn]; //维护dfs序的权值前缀和
int lowbit(int x) {return x&(-x);}
void add(int id,unsigned long long val){
for(int i=id;i<=n;i+=lowbit(i)){ //注意这里的n,需要根据题目要求改
s[i]+=val;
}
}
unsigned long long query(int id){
unsigned long long ans=0;
for(int i=id;i>=1;i-=lowbit(i)){
ans+=s[i];
}
return ans;
}
unsigned long long sum(int l,int r){
return query(r)-query(l-1);
}
void clear(){memset(s,0,sizeof(s));}
};
using namespace bit;
namespace blk{
unsigned long long val[400];
int block,num,belong[maxn];
void init_block(int n) {
block=sqrt(n);
num=n%block?n/block+1:n/block;
for(int i=1;i<=n;i++) belong[i]=(i-1)/block+1;
}
};
using namespace blk;
void dfs(int cur,int fa)
{
sta[belong[cur]]++;in[cur]=++tim;
for(int i=1;i<=num;i++) relate[cur][i]=sta[i];
for(int i=0;i<vec[cur].size();i++) if(vec[cur][i]!=fa) dfs(vec[cur][i],cur);
sta[belong[cur]]--;out[cur]=tim;
}
void update(int pos,int v)
{
for(int i=1;i<=num;i++) val[i]+=(unsigned long long)relate[pos][i]*v; //每个块的和更新
add(in[pos],v);
}
unsigned long long query(int l,int r)
{
unsigned long long res=0;
if(belong[r]-belong[l]<=1) {
for(int i=l;i<=r;i++) res+=sum(in[i],out[i]);
return res;
}
for(int i=l;i<=belong[l]*block;i++) res+=sum(in[i],out[i]);
for(int i=belong[l]+1;i<=belong[r]-1;i++) res+=val[i];
for(int i=(belong[r]-1)*block+1;i<=r;i++) res+=sum(in[i],out[i]);
return res;
}
int main()
{
scanf("%d %d",&n,&q);
init_block(n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1,u,v;i<=n;i++) {
scanf("%d %d",&u,&v);
if(!u) root=v;
else vec[u].push_back(v),vec[v].push_back(u);
}
dfs(root,0);
for(int i=1;i<=n;i++) update(i,a[i]);
for(int i=1,opt,l,r;i<=q;i++) {
scanf("%d %d %d",&opt,&l,&r);
if(opt==1) {
update(l,r-a[l]);
a[l]=r;
}else {
printf("%llu\n",query(l,r));
}
}
}