計蒜客區間整數操作
阿新 • • 發佈:2018-12-21
題目
給出 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
}