線段樹入門(poj 3274 3468 2528)

2021-06-23 08:53:25 字數 3465 閱讀 4629

概念:

在一類問題中,我們需要經常處理可以對映在乙個座標軸上的一些固定線段,例如說對映在ox軸上的線段。由於線段是可以互相覆蓋的,有時需要動態地取線段的並,例如取得並區間的總長度,或者並區間的個數等等。乙個線段是對應於乙個區間的,因此線段樹也可以叫做區間樹。

線段樹常用於區間多次插入查詢,經常改變資料。

而線段樹的核心在於如何設計乙個節點的資訊

這裡針對線段樹的應用有三個方面:

1. 每次查詢乙個區間的最大差值(結點存放這個區間的最大最小值 3274)

2.不斷修改區間內的數值,並查詢區間的和

這裡涉及到了修改,如果每次一修改就遞迴修改到葉子,效率非常低。所以在結點中設計乙個inc,即加量。這個區間每個元素都應該新增的。只有當查詢到了這個區間的時候才會遞迴修改下部的數值。

3.線段離散化。

離散化就是壓縮區間,使原有的長區間對映到新的短區間,但是區間壓縮前後的覆蓋關係不變。舉個例子:

有一條1到10的數軸(長度為9),給定4個區間[2,4] [3,6] [8,10] [6,9],覆蓋關係就是後者覆蓋前者,每個區間染色依次為 1 2 3 4。

現在我們抽取這4個區間的8個端點,2 4 3 6 8 10 6 9

然後刪除相同的端點,這裡相同的端點為6,則剩下2 4 3 6 8 10 9

對其公升序排序,得2 3 4 6 8 9 10

然後建立對映

2     3     4     6     8     9   10

1     2     3     4     5     6     7

那麼新的4個區間為 [1,3] [2,4] [5,7] [4,6],覆蓋關係沒有被改變。新數軸為1到7,即原數軸的長度從9壓縮到6,顯然構造[1,7]的線段樹比構造[1,10]的線段樹更省空間,搜尋也更快,但是求解的結果卻是一致的

離散化時有一點必須要注意的,就是必須先剔除相同端點後再排序,這樣可以減少參與排序元素的個數,節省時間。

資料儲存:

鏈式或者陣列形式,因為是類完全二叉樹所以可以用陣列儲存,且方便定位。假設區間元素有n,則總結點個數為2^(logn+1),實測發現一般4*n就夠了

poj 3274

/*

核心函式

1.buildtree :遞迴生成,自下向上建立。即利用下層資料。孩子結點為loc<<1,loc<<1|1

2.query:這個函式還是很有技巧性,每次查詢先分區間型別(包含,無交集,部分涵蓋) 無交集直接pass,包含則直接提取資訊。部分涵蓋則判斷與mid的關係。

看是否可以完全轉到另乙個分支中去

3.巧用位運算,加速程式。左右孩子分別為loc<<1,loc<<1|1

*/#include #include #define inf 10000000

#define maxn 200005

#define max(a,b) (a)>(b)?(a):(b)

#define min(a,b) (a)<(b)?(a):(b)

struct node

nodes[3*maxn];

int a[maxn];

int qmin,qmax;

void buildtree(int loc,int l,int r)

}void query(int loc,int l,int r)

else }

int main()

nodes[3*maxn];//結點設計;左右區間,待增加量,和值

int num[maxn];

int qmin,qmax;

void add(int i,int a,int b,__int64 c)

nodes[i].sum+=c*(b-a+1);//執行到這裡,表明這個是大區間,包含新增的區間所以sum是他們的個數之和

int mid=(nodes[i].l+nodes[i].r)>>1;

if(b<=mid) add(i<<1,a,b,c);

else if(a>mid) add(i<<1|1,a,b,c);

else

}__int64 buildtree(int loc,int l,int r)

return nodes[loc].sum;

}__int64 query(int loc,int l,int r)

else

return ans;

}int main()

else

}

} return 0;

}

poj 2528

離散化,剪枝

/*資料離散化經典使用

這裡注意離散化的時候不能因為怕浪費空間就用char,會溢位

離散化對映使用陣列內容->陣列下標

*/#include #include #include #include #define mm 10000000+200

#define maxn 20010

#define max(a,b) (a)>(b)?(a):(b)

#define min(a,b) (a)<(b)?(a):(b)

using namespace std;

struct node

nodes[4*maxn];

int num[maxn];

int qmin,qmax;

int cnt;

int ans;

int a[maxn],b[maxn];

int vis[mm];

void insert(int l,int r,int color,int loc)//從區間關係入手

//部分覆蓋

if(nodes[loc].color>=0)

//此節點為多色,先更新孩子的顏色,再為-1;

insert(l,r,color,loc<<1);

insert(l,r,color,loc<<1|1);

/*int mid=(nodes[loc].l+nodes[loc].r)>>1;

if(r<=mid) insert(l,r,color,loc<<1);

else if(l>mid) insert(l,r,color,loc<<1|1);

else

*/}void buildtree(int loc,int l,int r)

}void dfs(int loc)

}int main()

{ int n,q,i;

#ifndef online_judge//提交的時候把這段刪掉

freopen("input.txt","r",stdin);

freopen("output.txt","w",stdout);

#endif

scanf("%d",&n);

while(n--)

{scanf("%d",&q);

cnt=0;ans=0;

memset(vis,0,sizeof(vis));

for( i=0;i

線段樹入門 POJ 2182 Lots Cows

看別人的思路 從後往前推,排在最後一頭牛比他編號小的數量有x頭牛,那麼最後一頭牛的編號必然為x 1。按照這種思路下去,我們把1 n編號的牛排成一排。把x 1這頭牛從裡面刪除。假如倒數第二頭牛在他前面比他編號小的數量有y頭牛,那麼就在這1 n刪掉了求出來的牛後剩下的第y 1頭牛。方法一 直接搜尋 n ...

線段樹入門

線段樹 interval tree 是把區間逐次二分得到的一樹狀結構,它反映了包括歸併排序在內的很多分治演算法的問題求解方式。上圖是一棵典型的線段樹,它對區間 1,10 進行分割,直到單個點。這棵樹的特點 是 1.每一層都是區間 a,b 的乙個劃分,記 l b a 2.一共有log2l層 3.給定乙...

線段樹入門

學習下 線段樹的入門級 總結 線段樹是一種二叉搜尋樹,與區間樹相似,它將乙個區間劃分成一些單元區間,每個單元區間對應線段樹中的乙個葉結點。對於線段樹中的每乙個非葉子節點 a,b 它的左兒子表示的區間為 a,a b 2 右兒子表示的區間為 a b 2 1,b 因此線段樹是平衡二叉樹,最後的子節點數目為...