線段樹第二天,下午由於做了多校的第一場virtual judge,所以直到晚上纔有時間來搞這道題
除了早上做的那個區間染色的問題,這個問題應該也是個入門題,當然由於鄙人智商是硬傷,沒太反應明白,查瞭解題報告,再加上自己的一些嘗試,最終僥倖AC了,下面說說思路:
首先,更新區間的時候不可能更新區間裏的每一個點,這樣複雜度太高
因此,在update的時候,找到對應的區間之後,直接給該區間存儲一個變化值,我放在了add數組中,同時,對每個節點還維護了一個和,存在sum數組中,表示區間內所有數的和。push_up函數就是對父節點就子節點的和,push_down是重點,因爲更新是隻更新到區間的,那麼當下一次更新是對這個已經更新過的區間的子區間進行更新時,必須將他的變化量傳到子節點中去,舉個例子,假設我第一次對(2,6)區間加2,第二次對(3,4)區間加1,此時,在第一次操作後,只有(2,6)對應的add有值,其子區間對應的add都是爲0,所以,第二次去更新的時候,先把這個值傳遞到子區間中去,然後再向下找,這是爲了保證整棵樹維護的信息的正確性,而在父節點向子節點push_down的時候,可以對子節點的sum進行一下更新,以方便查找,具體的看push_down函數就可以體會。在對該節點的子節點插入後,得更新該節點的信息,即push_up一下
下面說查詢,查詢和update其實差不度,如果找到區間了,就直接返回區間的和,否則,還是得先push_down一下,再對子節點進行遞歸
以下是代碼:
#include <cstdio>
#include <cstring>
const int MAX = 100010;
long long sum[MAX<<2];
long long add[MAX<<2];
void push_up(int rt)
{
sum[rt] = sum[rt<<1]+sum[rt<<1|1];
}
void push_down(int l,int r,int rt)
{
if(add[rt])
{
add[rt<<1] += add[rt];
add[rt<<1|1] += add[rt];
int mid = l+r>>1;
sum[rt<<1] += (mid-l+1)*add[rt];
sum[rt<<1|1] += (r-mid)*add[rt];
add[rt] = 0;
}
}
void build(int l,int r,int rt)
{
if(l==r)
{
scanf("%lld",&sum[rt]);
return;
}
int mid = r+l>>1;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
push_up(rt);
}
void update(long long p,int L,int R,int l,int r,int rt)
{
if(L<=l&&R>=r)
{
add[rt]+=p;
sum[rt]+=p*(r-l+1);
return;
}
int mid = r+l>>1;
push_down(l,r,rt);
if(L<=mid) update(p,L,R,l,mid,rt<<1);
if(R>mid) update(p,L,R,mid+1,r,rt<<1|1);
push_up(rt);
}
long long query(int L,int R,int l,int r,int rt)
{
if(L<=l&&R>=r)
return sum[rt];
int mid = r+l>>1;
long long ans = 0;
push_down(l,r,rt);
if(L<=mid) ans+=query(L,R,l,mid,rt<<1);
if(R>mid) ans+=query(L,R,mid+1,r,rt<<1|1);
return ans;
}
int main()
{
int n,k;
while(scanf("%d%d",&n,&k)==2)
{
memset(add,0,sizeof(add));
build(1,n,1);
char c;
int x,y;
long long z;
while(k--)
{
scanf(" %c",&c);
if(c=='Q')
{
scanf("%d%d",&x,&y);
printf("%lld\n",query(x,y,1,n,1));
}
else
{
scanf("%d%d%lld",&x,&y,&z);
update(z,x,y,1,n,1);
}
}
}
return 0;
}