數列求和【線段樹基礎】

Enjoy_process發表於2018-09-02

 線段樹基礎題

操作包括:1.點修改 2.區間修改 3.區間查詢

// 線段樹基礎:數列求和 
#include<stdio.h>
#define maxn 10007//數列總個數
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
int sum[maxn<<2],add[maxn<<2];//sum求和,add為惰性標記
int a[maxn],n;//存原資料下標從1開始

//PushUp 更新結點資訊,這裡是求和 。用左右子結點更新父親結點 
void PushUp(int rt)
{ 
	sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}

//Build函式建樹
void Build(int l,int r,int rt)
{
	if(l==r){
		sum[rt]=a[l];
		return;
	}
	int m=(l+r)>>1;
	//左右遞迴
	Build(ls);
	Build(rs);
	//更新資訊 
	PushUp(rt); 
}

//點修改,使A[L]+=c
void Update1(int L,int c,int l,int r,int rt)
{
	if(l==r){//到達葉節點,修改 
		sum[rt]+=c;
		return;
	}
	int m=(l+r)>>1;
	//根據條件判斷往左子樹呼叫還是往右子樹呼叫
	if(L<=m) 
	  Update1(L,c,ls);
	else
	  Update1(L,c,rs);
	PushUp(rt);//子節點更新了,本結點也需要更新       
}
//下推標記函式
void PushDown(int rt,int ln,int rn)
{
	//ln,rn為左右子樹的數量
	if(add[rt]){
		//下推標記
		add[rt<<1]+=add[rt];
		add[rt<<1|1]+=add[rt];
		//修改子結點的sum是之與對應的add相對應
		sum[rt<<1]+=add[rt]*ln;
		sum[rt<<1|1]+=add[rt]*rn;
		//清除本結點標記
		add[rt]=0; 
	} 
 } 

//區間修改
void Update2(int L,int R,int C,int l,int r,int rt)
{
	if(L<=l&&r<=R){//如果本區間完全在操作區間[L,R]以內
		sum[rt]+=C*(r-l+1);//更新數字和,向上保持正確 
		add[rt]+=C;//增加add標記,表示本區間的sum正確 
		return; 
	}
	int m=(l+r)>>1;
	PushDown(rt,m-l+1,r-m);//下推標記
	//判斷左右子樹跟[L,R]有無交集,有交集才遞迴
	if(L<=m) 
	  Update2(L,R,C,ls);
	if(R>m) 
	  Update2(L,R,C,rs);
	PushUp(rt);//更新本結點資訊 
}

//區間查詢函式
int Query(int L,int R,int l,int r,int rt)
{
	if(L<=l&&r<=R)//在區間內,直接返回
	  return sum[rt];
	int m=(l+r)>>1;
	//下推標記,否則sum可能不正確
	PushDown(rt,m-l+1,r-m);
	//累計答案
	int ans=0;
	if(L<=m)
	  ans+=Query(L,R,ls);
	if(R>m)
	  ans+=Query(L,R,rs);
	return ans; 
}

int main()
{
	n=100;
	for(int i=1;i<=n;i++)
	  a[i]=i;
	//建樹
	Build(1,n,1);
	int ans=Query(1,100,1,n,1);
	printf("%d\n",ans);
	return 0;
	 
} 

 

相關文章