線段樹模板詳解

2021-08-03 19:51:50 字數 3188 閱讀 6290

原題:洛谷:

題目描述

如題,已知乙個數列,你需要進行下面兩種操作:

1.將某區間每乙個數加上x

2.求出某區間每乙個數的和

輸入輸出格式

輸入格式:

第一行包含兩個整數n、m,分別表示該數列數字的個數和操作的總個數。

第二行包含n個用空格分隔的整數,其中第i個數字表示數列第i項的初始值。

接下來m行每行包含3或4個整數,表示乙個操作,具體如下:

操作1: 格式:1 x y k 含義:將區間[x,y]內每個數加上k

操作2: 格式:2 x y 含義:輸出區間[x,y]內每個數的和

輸出格式:

輸出包含若干行整數,即為所有操作2的結果。

輸入輸出樣例

輸入樣例#1:

5 5

1 5 4 2 3

2 2 4

1 2 3 2

2 3 4

1 1 5 1

2 1 4

輸出樣例#1:

11 8

20 說明

時空限制:1000ms,128m

資料規模:

對於30%的資料:n<=8,m<=10

對於70%的資料:n<=1000,m<=10000

對於100%的資料:n<=100000,m<=100000

(資料已經過加強^_^,保證在int64/long long資料範圍內

**解析已經在**中呈現!!!

//線段樹,初學者適用 

//與二叉堆性質相似a的左兒子a[2*i]右兒子a[2*i+1],線

//段樹是將每個區間[l,r]分解成[l,m],[m+1,r],m=(l+r)/2

//直到l==r

#include

#include

#include

#include

#define maxn 1000000

using

namespace

std;

int x,y,k;

long

long add[maxn]/*表示每個點的標記(惰性標記)*/;

long

long a[maxn];//儲存最初的資料

struct nodetree[maxn];//儲存每個點左右端點和極值

//陣列模擬,空間開到4倍防止訪問越界

void buildtree(int i,int l,int r)//如果該點是葉節點

int mid=(l+r)>>1;

buildtree(i<<1,l,mid);//左移一位(二進位制) //左子樹

buildtree((i<<1)|1,mid+1,r);//右子樹

//按位或eg:3|2=0011|0010=0011

tree[i].sum=tree[i<<1].sum+tree[(i<<1)+1].sum;

//回溯維護區間和

}void pushdown(int i)

void update(int i,int x,int y,long

long k)

if(x<=tree[i].left && tree[i].right<=y)

//如果此處是完全有關區間

else

update(lc,x,y,k);

update(rc,x,y,k);

tree[i].sum=tree[lc].sum+tree[rc].sum;

}//二分新增

} long

long find(int i,int x,int y)

//當前節點的區間完全被目標區間包含

if(tree[i].left>y || tree[i].rightreturn

0; }

//完全在左子樹中or右子樹

if(add[i])

return find(lc,x,y)+find(rc,x,y);

//目標區間左右都有分布

}//重點詳解:

//void pushdown(int);//下放惰性標記

//此處標記指的是惰性標記,實際上是讓子節點暫時處於

//不更新狀態等到用到的時候在做更新而區間加時要把和

//那個區間有關的區間全部價值,不然不配合輸出

//超時了就要優化。

//你想啊,雖然x~y區間要增加乙個值,難道這個區間就一

//定要用嗎?

//如果區間值增加了但後來沒有詢問,我們一開始為什麼

//要增加呢?

//正如背古文,如果考試不考,我們為什麼要背呢?

//所以lazy思想就怎麼產生了。

//lazy,就是懶嘛,就是先不被古文,等到考試要考了再

//去背嘛;

//先不增加區間,等到詢問時在去增加嘛;

//我們可以搞乙個add陣列,專門把編號為num的區間要加

//的值記錄下來

//如果要用了,再給線段樹num加上v[num]的值,再把

//add[num]

//傳給v[num*2]和v[num*2+1];然後v[num]清零

//「如果要用了」這一步用乙個clean函式實現

//void buildtree(int,int,int);//構造線段樹

//void update(int,int,int,long long);//區間修改

//1,起始區間,終止區間,修改值

//long long find(int,int,int);//區間查詢

//1,區間起始值,區間終止值

int main()

buildtree(1,1,n);//構造線段樹

for(int i=0;iscanf("%d",&flag);

if(flag==1)

if(flag==2)

} return

0;

} //假設線段樹處理的最大長度是n,那麼總結點數不超過2*n

//線段樹分解數量級:線段樹能把任意一條長度為m的線段分為

//不超過2log(m)條線段,很大的數一下子變小,使得線段樹查

//詢與修改複雜度都在o(log2(n))範圍內能得以解決

//線段樹是一棵二叉樹,深度約為log2n左右

//所以線段樹空間消耗o(n),由於他的深度性質,解決問題效率

//更高

線段樹模板(模板)

參考部落格 持續更新。外鏈轉存失敗,源站可能有防盜煉機制,建議將儲存下來直接上傳 img xhrgdjcd 1613976863463 區間儲存在陣列中的下標對應為 12 3 4 5 6 7 8 9 10 11 12 13 14 15 四部分單點更新 根據題目的要求編寫自己的pushup,query...

線段樹模板

include include include using namespace std const int size 10010 struct node the node of line tree class linetree void updatem void updateline public ...

線段樹模板

單點更新,區間求最值 include include include include include define n 222222 using namespace std int num n struct tree tree n 4 void push up int root void build...