1. 程式人生 > >計蒜客區間整數操作

計蒜客區間整數操作

題目

給出 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 }