题目描述
前缀和(prefix sum) S i = ∑ k = 1 i a k S_i=\sum_{k=1}^i a_k Si=∑k=1iak
前前缀和(preprefix sum) 则把 S i S_i Si作为原序列再进行前缀和。记再次求得前缀和第 i i i个是 S S i SS_i SSi
给一个长度n的序列 a 1 , a 2 , ⋯ , a n a_1, a_2, \cdots, a_n a1,a2,⋯,an,有两种操作:
Modify i x:把 a i a_i ai改成x;
Query i:查询 S S i SS_i SSi
输入格式
第一行给出两个整数N,M。分别表示序列长度和操作个数
接下来一行有N个数,即给定的序列 a 1 , a 2 , . . . . a n a1,a2,....an a1,a2,....an
接下来M行,每行对应一个操作,格式见题目描述
输出格式
对于每个询问操作,输出一行,表示所询问的 S S i SS_i SSi的值。
输入输出样例
输入 #1
5 3
1 2 3 4 5
Query 5
Modify 3 2
Query 5
输出 #1
35
32
说明/提示
1<=N,M<=100000,且在任意时刻0<=Ai<=100000
解释,我们用线段树维护第一个前缀和。如果是操作Q,则查询1-i的求和,如果是M操作,则修改i-n的值,减去以前的再加上修改的
#include<iostream>
#define ll long long
#define N 100004
using namespace std;
ll tree[8*N]={0};
ll lazy[8*N]={0};
void push(int rt,int l,int r){
int mid=(l+r)/2;
tree[2*rt]+=lazy[rt]*(mid-l+1);
tree[2*rt+1]+=lazy[rt]*(r-mid);
lazy[2*rt]+=lazy[rt];
lazy[2*rt+1]+=lazy[rt];
lazy[rt]=0;
}
void update(int rt,int L,int R,int l,int r,ll v){
push(rt,L,R);
if(L>=l&&R<=r){
tree[rt]+=(R-L+1)*v;
lazy[rt]+=v;
return;
}
int mid=(L+R)/2;
if(l<=mid) update(2*rt,L,mid,l,r,v);
if(r>mid) update(2*rt+1,mid+1,R,l,r,v);
tree[rt]=tree[2*rt]+tree[2*rt+1];
}
ll query(int rt,int L,int R,int l,int r){
push(rt,L,R);
if(l<=L&&R<=r){
return tree[rt];
}
ll sum=0;
int mid=(L+R)/2;
if(l<=mid) sum+=query(2*rt,L,mid,l,r);
if(r>mid) sum+=query(2*rt+1,mid+1,R,l,r);
return sum;
}
char str[123];
ll c[N]={0};
int main(){
ios::sync_with_stdio(false);
int n=0,q=0;
cin>>n>>q;
for(ll i=1,sum=0;i<=n;i++){
cin>>c[i];sum+=c[i];update(1,1,n,i,i,sum);
}
ll a=0,b=0;
while(q--){
cin>>str;
if(str[0]=='Q'){
cin>>a;
cout<<query(1,1,n,1,a)<<endl;
}else{
cin>>a>>b;
update(1,1,n,a,n,b-c[a]);
c[a]=b;
}
}
return 0;
}