线段树,是一种可以在
O
(
l
o
g
n
)
O(logn)
O(logn)复杂度内维护区间操作的数据结构,它可以用于维护区间和,区间更新以及单点更新等等,使用起来也是非常灵活的。关于线段树的信息可以自己百度一下。我们先来看题。
传送门:
https://nanti.jisuanke.com/t/A2007
这道题要我们维护的是区间和,但这个区间和是带权的。它同时也要支持单点的更新。为了实现这一操作,我们首先想到的线段树。怎么维护呢?可以这样来想,首先,利用线段树维护区间的和
s
u
m
1
sum_1
sum1,在维护一个加权和
s
u
m
2
sum_2
sum2,这个加权和是这样的,对于
a
i
a_i
ai,我们维护
a
i
⋅
(
n
−
i
+
1
)
a_i \cdot(n-i+1)
ai⋅(n−i+1)的区间和。那么对于查询区间
[
l
,
r
]
[l,r]
[l,r],显然对于前缀是可以不用动的,只需要扣除后缀的和,答案为
s
u
m
2
−
s
u
m
1
⋅
(
n
−
r
)
sum_2-sum_1 \cdot(n-r)
sum2−sum1⋅(n−r)
AC代码实现
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
struct node{
int left,right;
ll sum1,sum2; //sum1初始和,sum2加权和;
}tree[4*maxn];
ll ans1,ans2;
void build(int l,int r,int k,int n){
tree[k].left=l;
tree[k].right=r;
if(l==r){
scanf("%d",&tree[k].sum1);
tree[k].sum2=tree[k].sum1*(n-l+1);
return;
}
int mid=(tree[k].left+tree[k].right)/2;
build(l,mid,2*k,n);
build(mid+1,r,2*k+1,n);
tree[k].sum1=tree[2*k].sum1+tree[2*k+1].sum1;
tree[k].sum2=tree[2*k].sum2+tree[2*k+1].sum2;
}
void update(int k,int x,int y,int n){ //更新修改值;
if(tree[k].left==tree[k].right){
tree[k].sum1=y; //把sum1的值修改为sum1;
tree[k].sum2=tree[k].sum1*(n-x+1);
return;
}
int mid=(tree[k].left+tree[k].right)/2;
if(x<=mid) update(2*k,x,y,n);
if(x>mid) update(2*k+1,x,y,n);
tree[k].sum1=tree[2*k].sum1+tree[2*k+1].sum1;
tree[k].sum2=tree[2*k].sum2+tree[2*k+1].sum2;
}
void query(int k,int x,int y){ //查询区间和;
if(tree[k].left>=x&&tree[k].right<=y){
ans1+=tree[k].sum1;
ans2+=tree[k].sum2;
return;
}
int mid=(tree[k].left+tree[k].right)/2;
if(x<=mid) query(2*k,x,y);
if(y>mid) query(2*k+1,x,y);
}
int main(){
int n,q;
scanf("%d %d",&n,&q);
build(1,n,1,n); //创建线段树;
int op,l,r;
while(q--){
scanf("%d %d %d",&op,&l,&r);
if(op==1){
ans1=ans2=0;
query(1,l,r);
ll ans=ans2-ans1*(n-r);
printf("%lld\n",ans);
}else if(op==2){
update(1,l,r,n);
}
}
return 0;
}
新的开始,每天都要快乐哈!