日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

线段树入门

發(fā)布時間:2023/11/27 生活经验 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 线段树入门 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

線段樹

?

?

前言

什么是線段樹

線段樹,是一種二叉搜索樹。它將一段區(qū)間劃分為若干單位區(qū)間,每一個節(jié)點(diǎn)都儲存著一個區(qū)間。它功能強(qiáng)大,支持區(qū)間求和,區(qū)間最大值,區(qū)間修改,單點(diǎn)修改等操作。

線段樹的思想和分治思想很相像。

線段樹的每一個節(jié)點(diǎn)都儲存著一段區(qū)間[L…R]的信息,其中葉子節(jié)點(diǎn)L=R。它的大致思想是:將一段大區(qū)間平均地劃分成2個小區(qū)間,每一個小區(qū)間都再平均分成2個更小區(qū)間……以此類推,直到每一個區(qū)間的L等于R(這樣這個區(qū)間僅包含一個節(jié)點(diǎn)的信息,無法被劃分)。通過對這些區(qū)間進(jìn)行修改、查詢,來實(shí)現(xiàn)對大區(qū)間的修改、查詢。

這樣一來,每一次修改、查詢的時間復(fù)雜度都只為O(logN),而空間復(fù)雜度一般開4N。

但是,可以用線段樹維護(hù)的問題必須滿足區(qū)間加法,否則是不可能將大問題劃分成子問題來解決的。

?證明:空間復(fù)雜度開4N

假設(shè):N=2h???

?

?

那么layer[0] = 20,layer[1] = 21+……+layer[h] = 2h

Sum = 20+21+22+……+2h ?= ?= 2*2h-1 = 2N-1

但是這只是特殊情況,大多數(shù)情況 N = 2h+M (M<2h

所有:2h < N < 2h+1???? 那么 ???2*2h-1 < Sum < 2*2h+1-1

那我們就取大的,開2*2h+1-1 = 4*2h-1 < 4N

?

?

什么是區(qū)間加法

一個問題滿足區(qū)間加法,僅當(dāng)對于區(qū)間[L,R]的問題的答案可以由[L,M]和[M+1,R]的答案合并得到。經(jīng)典的區(qū)間加法問題有:

滿足區(qū)間加法的例子:

1,區(qū)間和??? ????總區(qū)間和 = 左區(qū)間和+右區(qū)間和

2,最大公因數(shù) ???總GCD ?= GCD(左區(qū)間GCD,右區(qū)間GCD)

3,最大值 ???????總最大值 = MAX(左區(qū)間MAX,右區(qū)間MAX)

不滿足區(qū)間加法的例子:

1,區(qū)間眾數(shù) ??????????總區(qū)間總數(shù)無法根據(jù)左右區(qū)間總數(shù)求出來

2,最長遞增序列長度 ???總區(qū)間最長遞增長度無法直接由左右區(qū)間最長遞增長度相加而得

線段樹的原理及實(shí)現(xiàn)

注意:如果我沒有特別申明的話,這里的詢問全部都是區(qū)間求和

線段樹主要是把一段大區(qū)間平均地劃分成兩段小區(qū)間進(jìn)行維護(hù),再用小區(qū)間的值來更新大區(qū)間。這樣既能保證正確性,又能使時間保持在log級別(因?yàn)檫@棵線段樹是平衡的)。也就是說,一個[L…R]的區(qū)間會被劃分成[L…(L+R)/2]和[(L+R)/2+1…R]這兩個小區(qū)間進(jìn)行維護(hù),直到L=R。

下圖就是一棵[1, 5]的線段樹的分解過程(相同顏色的節(jié)點(diǎn)在同一層)

?

?

可以發(fā)現(xiàn),這棵線段樹的最大深度不超過[log2(n?1)]+2(其中[x]表示對x進(jìn)行下取整)

a.儲存方式

通常用的都是堆式儲存法,即編號為n的節(jié)點(diǎn)的左兒子編號為n?2,右兒子編號為n?2+1,父節(jié)點(diǎn)編號為n/2用位運(yùn)算優(yōu)化一下,以上的節(jié)點(diǎn)編號就變成:

左兒子:n << 1 右兒子:n << 1|1 父節(jié)點(diǎn):n >> 1

通常,每一個線段樹上的節(jié)點(diǎn)儲存的都是這幾個變量:區(qū)間左邊界,區(qū)間右邊界,區(qū)間的答案(這里為區(qū)間元素之和)和懶惰標(biāo)記

下面是線段樹的定義:

const int maxh = 4*N;     
struct node      
{      int l;      //左邊界     int r;      // 右邊界     int val, lazy;    //節(jié)點(diǎn)所維護(hù)的區(qū)間[l, r]的權(quán)值, 懶惰標(biāo)記      
}tree[maxh];//N為總節(jié)點(diǎn)數(shù)      
int val[maxh]; //原數(shù)組  

b.初始化

常見的做法是遍歷整棵線段樹,給每一個節(jié)點(diǎn)賦值,注意要遞歸到線段樹的葉節(jié)點(diǎn)才結(jié)束。

void pushup(int n)  
{  t[n].val = t[2*n].val+t[2*n+1].val;    //總區(qū)間和 = 左區(qū)間和+右區(qū)間和   
}  
void build(int n,int l,int r)  
{  t[n].l = l;     //記錄維護(hù)的原數(shù)組的左端點(diǎn)   t[n].r = r;     //記錄維護(hù)的右端點(diǎn)   t[n].lazy = 0;  //標(biāo)記下懶惰數(shù)組   if(l==r){       //l==r,表示葉子節(jié)點(diǎn),  t[n].val = val[l];    //因?yàn)閘==r,那么這個節(jié)點(diǎn)維護(hù)的值就是原數(shù)組val[l]的值   return;  }         int mid = (l+r)>>1;  build(n*2,l,mid);      //遞歸建左子樹   build(n*2+1,mid+1,r);  //遞歸建右子樹  pushup(n);             //求該點(diǎn)的權(quán)值   
} 

?

?

c.單點(diǎn)修改

時間復(fù)雜度:O(log2(N))

線段樹的高度接近log2(N):所以更新經(jīng)過的節(jié)點(diǎn)數(shù)接近log2(N)?

當(dāng)我們要把下標(biāo)為idx的元素修改(加減乘除、賦值運(yùn)算等),這里我們加上一個值C,可以遞歸向下找到葉結(jié)點(diǎn),再向上更新,左節(jié)點(diǎn)維護(hù)的范圍是[l, mid],右節(jié)點(diǎn)維護(hù)的范圍是[mid+1, r]。

如果:idx<=mid,那么val[idx]就由左節(jié)點(diǎn)維護(hù),所有走左節(jié)點(diǎn)

如果:mid<idx,那么val[idx]由右節(jié)點(diǎn)維護(hù),所有走右節(jié)點(diǎn)

最后直到l==r的時候找到葉子節(jié)點(diǎn),這個節(jié)點(diǎn)維護(hù)的范圍就是[idx, idx]即val[idx]這個元素

修改這個葉子節(jié)點(diǎn)的權(quán)值,最后回溯向上修改沿途的節(jié)點(diǎn)權(quán)值

void updateOne(int n,int idx,int C)  
{  int l = t[n].l;//左端點(diǎn)   int r = t[n].r;//右端點(diǎn)   if(l==r)    //l==r,到了葉子節(jié)點(diǎn)   
    {  t[n].val += C;    //更新權(quán)值   return;   }     int mid = (l+r)>>1;  if(idx<=mid)     //val[idx]由左節(jié)點(diǎn)維護(hù)   updateOne(n*2,idx,C);  else          //val[idx]由右節(jié)點(diǎn)維護(hù)   updateOne(n*2+1,idx,C);  pushup(n);    //向上更新   
}   

?

? ?

?

模擬更新[2, 5],從n=1開始

第一輪:節(jié)點(diǎn)2:[1, 3]與待更新范圍有交集要走,節(jié)點(diǎn)3:[4, 5]與待更新范圍有交集要走

第二輪:節(jié)點(diǎn)4:[1, 2]有交集要走,節(jié)點(diǎn)5:[3, 3]有交集要走,節(jié)點(diǎn)[4, 5]被更新范圍包括直接更新,lazy+=1不向下走。

第三輪:節(jié)點(diǎn)1:[1, 1]沒有交集排除,節(jié)點(diǎn)9:[2, 2]被包括更新,節(jié)點(diǎn)3:[3, 3]被包括更新

結(jié)束!

?

時間復(fù)雜度:O(log2N)

定理:n>=3時,一個[1,n]的線段樹可以將[1,n]的任意子區(qū)間[L,R]分解為不超過2|log2(N-1)| 向下取整子區(qū)間。

?

d.區(qū)間修改

待更新區(qū)間為[L, R],左節(jié)點(diǎn)維護(hù)的區(qū)間[l, mid], 右節(jié)點(diǎn)維護(hù)的區(qū)間[mid+1, r]

L<=mid,左節(jié)點(diǎn)維護(hù)[l, mid]與[L, R]有交集要走

mid<R,?? 右節(jié)點(diǎn)維護(hù)[mid+1, r]與[L, R]有交集要走

如果[l, r]被[L, R],直接更新,更新一下lazy標(biāo)記,不往下走

遞歸進(jìn)行直到更新結(jié)束

?

這里就要引入一樣新的神奇的東西——懶惰標(biāo)記!

懶惰標(biāo)記

標(biāo)記的含義:本區(qū)間已經(jīng)被更新過了,但是子區(qū)間卻沒有被更新過,但是子結(jié)點(diǎn)暫時不更新,使用到時再更新,懶惰標(biāo)記用于記錄更新的信息(如果區(qū)間求和只用記錄加了多少,而區(qū)間加減乘除等多種操作的問題則要記錄進(jìn)行的是哪一種操作,可能記錄的信息會復(fù)雜一些)

這里再引入兩個很重要的東西:相對標(biāo)記和絕對標(biāo)記。

相對標(biāo)記&絕對標(biāo)記

相對標(biāo)記指的是可以共存的標(biāo)記,且打標(biāo)記的順序與答案無關(guān),即標(biāo)記可以疊加。 比如說給一段區(qū)間中的所有數(shù)字都+a,我們就可以把標(biāo)記疊加一下,比如上一次打了一個+1的標(biāo)記,這一次要給這一段區(qū)間+2,那么就把+1的標(biāo)記變成+3。

絕對標(biāo)記是指不可以共存的標(biāo)記,每一次都要先把標(biāo)記下傳,再給當(dāng)前節(jié)點(diǎn)打上新的標(biāo)記。這些標(biāo)記不能改變次序,否則會出錯。 比如說給一段區(qū)間的數(shù)字重新賦值,或是給一段區(qū)間進(jìn)行多種操作。

有了懶惰標(biāo)記這種神奇的東西,我們區(qū)間修改時就可以偷一下懶,先修改當(dāng)前節(jié)點(diǎn),然后直接把修改信息掛在節(jié)點(diǎn)上就可以了!

如下面這棵線段樹,當(dāng)我們要修改區(qū)間[1…4],將元素賦值為1時,我們可以先找到所有的整個區(qū)間都要被修改的節(jié)點(diǎn),顯然是儲存區(qū)間[1…3]和[4…4]的這兩個節(jié)點(diǎn)。我們就可以先把[1…3]的sum改為3((3?1+1)?1=3,把[4…4]的sum改1((1?1+1)?1=1 然后給它們打上值為1的懶惰標(biāo)記,然后就可以了。

比如下面這顆線段樹,當(dāng)我們修改[1, 4]區(qū)間都+1時我們先找到要修改的節(jié)點(diǎn),就是t[2]和t[6],之后修改權(quán)值,打上標(biāo)記

t[2]={???????????????????????????????????????????? t[6] = {

????? l = 1;??????????????????????????????????????? ????????????????? l = 4;???

????? r = 3;???????????????????????????????????????????????????????? r = 4;??

????? val = 9;????????????????????????????????????????????????????? val = 5;

????? lazy = 1;??????????????????????????????????????????????????? lazy = 1;???

}?????????????????????????????????????????????????????????? }????

?

?

?

這樣一來,我們每一次修改區(qū)間時只要找到目標(biāo)區(qū)間就可以了,不用再向下遞歸到葉節(jié)點(diǎn)。

下面是區(qū)間[L, R]+C的代碼

void pushdown(int n)  
{  int l = t[n].l;  int r = t[n].r;  if(l==r)//葉子節(jié)點(diǎn)沒有子結(jié)點(diǎn)   return;  if(t[n].lazy)//懶惰標(biāo)記不為0才能向下更新   
    {     int mid = (l+r)/2;  t[2*n].val += t[n].lazy*(mid-l+1);//更新左節(jié)點(diǎn)權(quán)值   t[2*n+1].val += t[n].lazy*(r-mid);//更新右節(jié)點(diǎn)權(quán)值  t[2*n].lazy += t[n].lazy;         //更新左節(jié)點(diǎn)標(biāo)記   t[2*n+1].lazy += t[n].lazy;       //更新右節(jié)點(diǎn)標(biāo)記   t[n].lazy = 0;                    //清除標(biāo)記      
    }  
}  
void updateRange(int n,int L,int R,int C)  
{  int l = t[n].l;  int r = t[n].r;  if(L<=l && r<=R)    //待更新區(qū)間為[L, R],而[l, r]是[L, R]的子集所以更新   
    {  t[n].val += (r-l+1)*C;    //更新權(quán)值   t[n].lazy += C;           //更新標(biāo)記      return;  }  pushdown(n);    //向下更新   int mid = (l+r)>>1;  if(L<=mid)      //左節(jié)點(diǎn)維護(hù)的區(qū)間與[L, R]有交集   updateRange(2*n,L,R,C);  if(R>mid)      //右節(jié)點(diǎn)維護(hù)的區(qū)間與[L, R]有交集   updateRange(2*n+1,L,R,C);  pushup(n);     //向上更新   
}  

?

e.區(qū)間查詢

和區(qū)間修改一樣的道理,這里是直接返回結(jié)果,區(qū)間修改是修改權(quán)值

int query(int n,int L,int R)  
{  int l = t[n].l;  int r = t[n].r;  if(L<=l && r<=R)    //被包括直接返回   return t[n].val;  pushdown(n);        //向下更新   int ans = 0;        //保持答案   int mid = (l+r)>>1;  if(L<=mid)           //查詢區(qū)間與左節(jié)點(diǎn)維護(hù)區(qū)間有交集   ans += query(2*n,L,R);//加上左節(jié)點(diǎn)交集區(qū)間的答案   if(R>mid)  ans += query(2*n+1,L,R);//加上右節(jié)點(diǎn)交集區(qū)間的答案   return ans;  
}  

?

模板:

#include <iostream>  
using namespace std;  
const int N = 1e5+10;  
const int maxh = 4*N;     
struct node      
{      int l;      //左邊界     int r;      // 右邊界     int val, lazy;    //節(jié)點(diǎn)所維護(hù)的區(qū)間[l, r]的權(quán)值, 懶惰標(biāo)記      
}t[maxh];//N為總節(jié)點(diǎn)數(shù)      
int val[maxh]; //原數(shù)組   void pushup(int n)  
{  t[n].val = t[2*n].val+t[2*n+1].val;    //總區(qū)間和 = 左區(qū)間和+右區(qū)間和   
}  
void build(int n,int l,int r)  
{  t[n].l = l;     //記錄維護(hù)的原數(shù)組的左端點(diǎn)   t[n].r = r;     //記錄維護(hù)的右端點(diǎn)   t[n].lazy = 0;  //標(biāo)記下懶惰數(shù)組   if(l==r){       //l==r,表示葉子節(jié)點(diǎn),  t[n].val = val[l];    //因?yàn)閘==r,那么這個節(jié)點(diǎn)維護(hù)的值就是原數(shù)組val[l]的值   return;  }         int mid = (l+r)>>1;  build(n*2,l,mid);      //遞歸建左子樹   build(n*2+1,mid+1,r);  //遞歸建左子樹  pushup(n);             //求該點(diǎn)的權(quán)值   
}  
void updateOne(int n,int idx,int C)  
{  int l = t[n].l;//左端點(diǎn)   int r = t[n].r;//右端點(diǎn)   if(l==r)    //l==r,到了葉子節(jié)點(diǎn)   
    {  t[n].val += C;    //更新權(quán)值   return;   }     int mid = (l+r)>>1;  if(idx<=mid)     //val[idx]由左節(jié)點(diǎn)維護(hù)   updateOne(n*2,idx,C);  else          //val[idx]由右節(jié)點(diǎn)維護(hù)   updateOne(n*2+1,idx,C);  pushup(n);    //向上更新   
}   
void pushdown(int n)  
{  int l = t[n].l;  int r = t[n].r;  if(l==r)//葉子節(jié)點(diǎn)沒有子結(jié)點(diǎn)   return;  if(t[n].lazy)//懶惰標(biāo)記不為0才能向下更新   
    {     int mid = (l+r)/2;  t[2*n].val += t[n].lazy*(mid-l+1);//更新左節(jié)點(diǎn)權(quán)值   t[2*n+1].val += t[n].lazy*(r-mid);//更新右節(jié)點(diǎn)權(quán)值  t[2*n].lazy += t[n].lazy;         //更新左節(jié)點(diǎn)標(biāo)記   t[2*n+1].lazy += t[n].lazy;       //更新右節(jié)點(diǎn)標(biāo)記   t[n].lazy = 0;                    //清除標(biāo)記      
    }  
}  
int query(int n,int L,int R)  
{  int l = t[n].l;  int r = t[n].r;  if(L<=l && r<=R)    //被包括直接返回   return t[n].val;  pushdown(n);        //向下更新   int ans = 0;        //保持答案   int mid = (l+r)>>1;  if(L<=mid)           //查詢區(qū)間與左節(jié)點(diǎn)維護(hù)區(qū)間有交集   ans += query(2*n,L,R);//加上左節(jié)點(diǎn)交集區(qū)間的答案   if(R>mid)  ans += query(2*n+1,L,R);//加上右節(jié)點(diǎn)交集區(qū)間的答案   return ans;  
}  
void updateRange(int n,int L,int R,int C)  
{  int l = t[n].l;  int r = t[n].r;  if(L<=l && r<=R)    //待更新區(qū)間為[L, R],而[l, r]是[L, R]的子集所以更新   
    {  t[n].val += (r-l+1)*C;    //更新權(quán)值   t[n].lazy += C;           //更新標(biāo)記      return;  }  pushdown(n);    //向下更新   int mid = (l+r)>>1;  if(L<=mid)      //左節(jié)點(diǎn)維護(hù)的區(qū)間與[L, R]有交集   updateRange(2*n,L,R,C);  if(R>mid)      //右節(jié)點(diǎn)維護(hù)的區(qū)間與[L, R]有交集   updateRange(2*n+1,L,R,C);  pushup(n);     //向上更新   
}  
int main()  
{  return 0;   
}  

?

?

?

例題

HDU1166

https://vjudge.net/problem/HDU-1166

單點(diǎn)更新+區(qū)間查詢

?

Input

第一行一個整數(shù)T,表示有T組數(shù)據(jù)。
每組數(shù)據(jù)第一行一個正整數(shù)N(N<=50000),表示敵人有N個工兵營地,接下來有N個正整數(shù),第i個正整數(shù)ai代表第i個工兵營地里開始時有ai個人(1<=ai<=50)。
接下來每行有一條命令,命令有4種形式:
(1) Add i j,i和j為正整數(shù),表示第i個營地增加j個人(j不超過30)
(2)Sub i j ,i和j為正整數(shù),表示第i個營地減少j個人(j不超過30);
(3)Query i j ,i和j為正整數(shù),i<=j,表示詢問第i到第j個營地的總?cè)藬?shù);
(4)End 表示結(jié)束,這條命令在每組數(shù)據(jù)最后出現(xiàn);
每組數(shù)據(jù)最多有40000條命令

Output

對第i組數(shù)據(jù),首先輸出“Case i:”和回車,
對于每個Query詢問,輸出一個整數(shù)并回車,表示詢問的段中的總?cè)藬?shù),這個數(shù)保持在int以內(nèi)。

#include <iostream>
using namespace std;
const int N = 5e5+10;
const int maxh = 4*N;   
struct node    
{    int l;      //左邊界   int r;      // 右邊界   int val;    //節(jié)點(diǎn)所維護(hù)的區(qū)間[l, r]的權(quán)值 
}t[maxh];//N為總節(jié)點(diǎn)數(shù)    
int val[maxh]; //原數(shù)組 void pushup(int n)
{t[n].val = t[2*n].val+t[2*n+1].val;    //總區(qū)間和 = 左區(qū)間和+右區(qū)間和 
}
void build(int n,int l,int r)
{t[n].l = l;     //記錄維護(hù)的原數(shù)組的左端點(diǎn) t[n].r = r;     //記錄維護(hù)的右端點(diǎn) if(l==r){       //l==r,表示葉子節(jié)點(diǎn),t[n].val = val[l];    //因?yàn)閘==r,那么這個節(jié)點(diǎn)維護(hù)的值就是原數(shù)組val[l]的值 return;}       int mid = (l+r)>>1;build(n*2,l,mid);      //遞歸建左子樹 build(n*2+1,mid+1,r);  //遞歸建左子樹pushup(n);             //求該點(diǎn)的權(quán)值 
}
void updateOne(int n,int idx,int C)
{int l = t[n].l;//左端點(diǎn) int r = t[n].r;//右端點(diǎn) if(l==r)    //l==r,到了葉子節(jié)點(diǎn) 
    {t[n].val += C;    //更新權(quán)值 return; }   int mid = (l+r)>>1;if(idx<=mid)     //val[idx]由左節(jié)點(diǎn)維護(hù) updateOne(n*2,idx,C);else          //val[idx]由右節(jié)點(diǎn)維護(hù) updateOne(n*2+1,idx,C);pushup(n);    //向上更新 
} 
int query(int n,int L,int R)
{int l = t[n].l;int r = t[n].r;if(L<=l && r<=R)    //被包括直接返回 return t[n].val;int ans = 0;        //保持答案 int mid = (l+r)>>1;if(L<=mid)          //查詢區(qū)間與左節(jié)點(diǎn)維護(hù)區(qū)間有交集 ans += query(2*n,L,R);//加上左節(jié)點(diǎn)交集區(qū)間的答案 if(R>mid)ans += query(2*n+1,L,R);//加上右節(jié)點(diǎn)交集區(qū)間的答案 return ans;
}
int main()
{int T;scanf("%d", &T);for(int cnt = 1;cnt <= T;cnt ++){int N;char op[10];printf("Case %d:\n", cnt);scanf("%d", &N);for(int i = 1;i <= N;i ++)scanf("%d", &val[i]);    //數(shù)組的權(quán)值 build(1,1,N);                //建樹 while(scanf("%s", op))      {if(op[0]=='E')          //END結(jié)束     break;int i, j;scanf("%d%d", &i, &j);if(op[0]=='A')          //ADD,第i個營地+j人 updateOne(1,i,j);else if(op[0]=='S')     //ADD,第i個營地-j人updateOne(1,i,-j);elseprintf("%d\n", query(1,i,j));//查詢i--j營地一共有多少人 
        }}return 0; 
}

?

?

?

HDU1754

https://vjudge.net/problem/HDU-1754

Input

多組測試,EOF結(jié)束

第一行,有兩個正整數(shù) N 和 M ( 0<N<=200000,0<M<5000 ),分別代表學(xué)生的數(shù)目和操作的數(shù)目。?
學(xué)生ID編號分別從1編到N。?
第二行包含N個整數(shù),代表這N個學(xué)生的初始成績,其中第i個數(shù)代表ID為i的學(xué)生的成績。?
接下來有M行。每一行有一個字符 C (只取'Q'或'U') ,和兩個正整數(shù)A,B。?
當(dāng)C為'Q'的時候,表示這是一條詢問操作,它詢問ID從A到B(包括A,B)的學(xué)生當(dāng)中,成績最高的是多少。?
當(dāng)C為'U'的時候,表示這是一條更新操作,要求把ID為A的學(xué)生的成績更改為B。?

?

Output

對于每一次詢問操作,在一行里面輸出最高成績。

?

#include <iostream> 
#include <cmath> 
using namespace std;  
const int N = 2e5+10;  
const int maxh = 4*N;     
struct node      
{      int l;      //左邊界     int r;      // 右邊界     int val;    //節(jié)點(diǎn)所維護(hù)的區(qū)間[l, r]的權(quán)值    
}t[maxh];//N為總節(jié)點(diǎn)數(shù)      
int val[maxh]; //原數(shù)組   void pushup(int n)  
{  t[n].val = max(t[2*n].val, t[2*n+1].val);    //總區(qū)間和 = 左區(qū)間和+右區(qū)間和   
}  
void build(int n,int l,int r)  
{  t[n].l = l;     //記錄維護(hù)的原數(shù)組的左端點(diǎn)   t[n].r = r;     //記錄維護(hù)的右端點(diǎn)   if(l==r){       //l==r,表示葉子節(jié)點(diǎn),  t[n].val = val[l];    //因?yàn)閘==r,那么這個節(jié)點(diǎn)維護(hù)的值就是原數(shù)組val[l]的值   return;  }         int mid = (l+r)>>1;  build(n*2,l,mid);      //遞歸建左子樹   build(n*2+1,mid+1,r);  //遞歸建左子樹  pushup(n);             //求該點(diǎn)的權(quán)值   
}  
void updateOne(int n,int idx,int C)  
{  int l = t[n].l;//左端點(diǎn)   int r = t[n].r;//右端點(diǎn)   if(l==r)    //l==r,到了葉子節(jié)點(diǎn)   
    {  t[n].val = C;    //更新權(quán)值   return;   }     int mid = (l+r)>>1;  if(idx<=mid)     //val[idx]由左節(jié)點(diǎn)維護(hù)   updateOne(n*2,idx,C);  else          //val[idx]由右節(jié)點(diǎn)維護(hù)   updateOne(n*2+1,idx,C);  pushup(n);    //向上更新   
}   int query(int n,int L,int R)  
{  int l = t[n].l;  int r = t[n].r;  if(L<=l && r<=R)    //被包括直接返回   return t[n].val;  int ans = 0;        //保持答案   int mid = (l+r)>>1;  if(L<=mid)           //查詢區(qū)間與左節(jié)點(diǎn)維護(hù)區(qū)間有交集   ans = max(ans, query(2*n,L,R));//加上左節(jié)點(diǎn)交集區(qū)間的答案   if(R>mid)  ans = max(ans, query(2*n+1,L,R));//加上右節(jié)點(diǎn)交集區(qū)間的答案   return ans;  
}  int main()  
{  int N, M;while(scanf("%d%d", &N, &M)!=EOF){for(int i = 1;i <= N;i ++)scanf("%d", &val[i]);build(1,1,N);while(M--){char C; int A, B;getchar();scanf("%c%d%d", &C, &A, &B);if(C=='Q')printf("%d\n", query(1,A,B));elseupdateOne(1,A,B);}}return 0;   
}  

?

?

?

HDU1698

https://vjudge.net/problem/HDU-1698

Input

題目有多組輸入:

第一行輸入一個整數(shù)T,表示數(shù)據(jù)組數(shù)

對于每組輸入:

第一行整數(shù)N:繩子有多少段,而默認(rèn)每段繩子價值為1

第二行整數(shù)Q:表示更新操作的次數(shù)

接下來Q行每行輸入:X Y Z:表示將[X, Y]段變?yōu)閮r值Z

?

Output

輸出繩子的總價值

#include <iostream>  
using namespace std;  
const int N = 1e5+10;  
const int maxh = 4*N;     
struct node      
{      int l;      //左邊界     int r;      // 右邊界     int val, lazy;    //節(jié)點(diǎn)所維護(hù)的區(qū)間[l, r]的權(quán)值, 懶惰標(biāo)記      
}t[maxh];//N為總節(jié)點(diǎn)數(shù)      void pushup(int n)  
{  t[n].val = t[2*n].val+t[2*n+1].val;    //總區(qū)間和 = 左區(qū)間和+右區(qū)間和   
}  
void build(int n,int l,int r)  
{  t[n].l = l;     //記錄維護(hù)的原數(shù)組的左端點(diǎn)   t[n].r = r;     //記錄維護(hù)的右端點(diǎn)   t[n].lazy = 0;  //標(biāo)記下懶惰數(shù)組   if(l==r){       //l==r,表示葉子節(jié)點(diǎn),  t[n].val = 1;    //因?yàn)閘==r,那么這個節(jié)點(diǎn)維護(hù)的值就是原數(shù)組val[l]的值   return;  }         int mid = (l+r)>>1;  build(n*2,l,mid);      //遞歸建左子樹   build(n*2+1,mid+1,r);  //遞歸建左子樹  pushup(n);             //求該點(diǎn)的權(quán)值   
}  
void pushdown(int n)  
{  int l = t[n].l;  int r = t[n].r;  if(l==r)//葉子節(jié)點(diǎn)沒有子結(jié)點(diǎn)   return;  if(t[n].lazy)//懶惰標(biāo)記不為0才能向下更新   
    {     int mid = (l+r)/2;  t[2*n].val = t[n].lazy*(mid-l+1);//更新左節(jié)點(diǎn)權(quán)值   t[2*n+1].val = t[n].lazy*(r-mid);//更新右節(jié)點(diǎn)權(quán)值  t[2*n].lazy = t[n].lazy;         //更新左節(jié)點(diǎn)標(biāo)記   t[2*n+1].lazy = t[n].lazy;       //更新右節(jié)點(diǎn)標(biāo)記   t[n].lazy = 0;                    //清除標(biāo)記      
    }  
}  
void updateRange(int n,int L,int R,int C)  
{  int l = t[n].l;  int r = t[n].r;  if(L<=l && r<=R)    //待更新區(qū)間為[L, R],而[l, r]是[L, R]的子集所以更新   
    {  t[n].val = (r-l+1)*C;    //更新權(quán)值   t[n].lazy = C;           //更新標(biāo)記      return;  }  pushdown(n);    //向下更新   int mid = (l+r)>>1;  if(L<=mid)      //左節(jié)點(diǎn)維護(hù)的區(qū)間與[L, R]有交集   updateRange(2*n,L,R,C);  if(R>mid)      //右節(jié)點(diǎn)維護(hù)的區(qū)間與[L, R]有交集   updateRange(2*n+1,L,R,C);  pushup(n);     //向上更新   
}  
int main()  
{  int T, cnt = 1;scanf("%d", &T);while(T--){int N, Q;scanf("%d", &N);build(1,1,N);scanf("%d", &Q);while(Q--){int X, Y, Z;scanf("%d%d%d", &X, &Y, &Z);updateRange(1,X,Y,Z);}printf("Case %d: The total value of the hook is %d.\n", cnt++, t[1].val);   }return 0;   
}  

?

?

?

轉(zhuǎn)載于:https://www.cnblogs.com/YukiNote/p/11301577.html

總結(jié)

以上是生活随笔為你收集整理的线段树入门的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。