版权声明:原创博客 喜欢拿走 https://blog.csdn.net/qq_43004519/article/details/84960049
题目
给出 N 个整数 A1,A2,…,AN,你需要处理区间加,区间求和。
输入格式
第一行两个整数 N 和 Q (1≤N,Q≤10^5)。
第二行 N 个整数,表示 A1,A2…AN(∣Ai∣≤10^9)的初始值。
接下来 Q 行,每行一个操作:
C a b c,表示 Aa,Aa+1…Ab 每个数加 c (∣c∣≤10000)。
Q a b,表示询问 Aa,Aa+1…Ab 的和,答案可能超过 32 位整数。
输出格式
对于所有 Q 询问,一行输出一个答案。
分析
线段树配合区间更新就可以ko这道题,不过本人在一个地方跌坑里了。
lazy[p]+=v;
lazy[p]=v;
一个有加号,一个没的加号,同时对一个区间操作两次不同的数,第二种写法是过不了的!幸好爬出来了。
AC
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const ll MAXN=1e5+1;
ll s[4*MAXN],lazy[4*MAXN];
void up(ll p){//更新值
s[p]=s[p*2]+s[p*2+1];
}
void down(ll p,ll l,ll r){//若有标记,则更新子节点的值,并给子节点标记,去除该节点的标记
if(lazy[p]){
ll mid=(l+r)/2;
s[p*2]+=lazy[p]*(mid-l+1);
s[p*2+1]+=lazy[p]*(r-mid);
lazy[p*2]+=lazy[p];
lazy[p*2+1]+=lazy[p];
lazy[p]=0;
}
}
void modify(ll p,ll l,ll r,ll x,ll y,ll v){//更新结点区间【x,y】及以上节点区间的值
//cout<<"进入modify,区间【l,r】l="<<l<<" r="<<r<<endl;
if(x<=l&&r<=y){
s[p]+=(r-l+1)*v;
lazy[p]+=v;
return;
}
down(p,l,r);
ll mid=(l+r)/2;
//cout<<" mid="<<mid<<" x="<<x<<" y="<<y<<endl;
if(x<=mid)
modify(p*2,l,mid,x,y,v);
if(y>mid)
modify(p*2+1,mid+1,r,x,y,v);
up(p);
return;
}
ll query(ll p,ll l,ll r,ll x,ll y){
if(x<=l&&r<=y){
return s[p];
}
down(p,l,r);
ll mid=(l+r)/2,ans=0;
if(x<=mid){
ans+=query(p*2,l,mid,x,y);
}
if(y>mid){
ans+=query(p*2+1,mid+1,r,x,y);
}
return ans;
}
int main()
{
ll n,q;
cin>>n>>q;
ll d,x,y,v;
char c;
for(ll i=1;i<=n;i++){//将整个树的初始数据完善
scanf("%lld",&d);
modify(1,1,n,i,i,d);
}
while(q--){
cin>>c;
if(c=='Q'){
//cin>>x>>y;
scanf("%lld%lld",&x,&y);
printf("%lld\n",query(1,1,n,x,y));
}
else{
//cin>>x>>y>>v;
scanf("%lld%lld%lld",&x,&y,&v);
modify(1,1,n,x,y,v);
}
}
return 0;//give me five
}