POJ2182題解——線段樹

POJ2182題解——線段樹ios

2019-12-20數組

by juruoOIer優化

1.線段樹簡介(來源:百度百科)ui

線段樹是一種二叉搜索樹,與區間樹類似,它將一個區間劃分紅一些單元區間,每一個單元區間對應線段樹中的一個葉結點。
使用線段樹能夠快速的查找某一個節點在若干條線段中出現的次數,時間複雜度爲O(logN)。而未優化的空間複雜度爲2N,實際應用時通常還要開4N的數組以避免越界,所以有時須要離散化讓空間壓縮。
 
2.線段樹實際應用
上面的都是些基本的線段樹結構,但只有這些並不能作什麼,就比如一個程序有輸入沒輸出,根本沒有任何用處。
 
最簡單的應用就是記錄線段是否被覆蓋,隨時查詢當前被覆蓋線段的總長度。那麼此時能夠在結點結構中加入一個變量int count;表明當前結點表明的子樹中被覆蓋的線段長度和。這樣就要在插入(刪除)當中維護這個count值,因而當前的覆蓋總值就是根節點的count值了。
 
另外也能夠將count換成bool cover;支持查找一個結點或線段是否被覆蓋。
 
實際上,經過在結點上記錄不一樣的數據,線段樹還能夠完成不少不一樣的任務。例如,若是每次插入操做是在一條線段上每一個位置均加k,而查詢操做是計算一條線段上的總和,那麼在結點上須要記錄的值爲sum。
 
這裏會遇到一個問題:爲了使全部sum值都保持正確,每一次插入操做可能要更新O(N)個sum值,從而使時間複雜度退化爲O(N)。
 
解決方案是Lazy思想:對整個結點進行的操做,先在結點上作標記,而並不是真正執行,直到根據查詢操做的須要分紅兩部分。
 
根據Lazy思想,咱們能夠在不表明原線段的結點上增長一個值toadd,即爲對這個結點,留待之後執行的插入操做k值的總和。對整個結點插入時,只更新sum和toadd值而不向下進行,這樣時間複雜度可證實爲O(logN)。
 
對一個toadd值爲0的結點整個進行查詢時,直接返回存儲在其中的sum值;而若對toadd不爲0的一部分進行查詢,則要更新其左右子結點的sum值,而後把toadd值傳遞下去,再對這個查詢自己,左右子結點分別遞歸下去。時間複雜度也是O(nlogN)。
 
3.代碼實現
下面先給出線段樹建樹的代碼:
void build_tree(int l,int r,int k)     
{
    tree[k].l=l;
    tree[k].r=r;
    tree[k].sum=r-l+1;
    if(l==r) return ;
    int m=(l+r)>>1;
    build_tree(l,m,k<<1);
    build_tree(m+1,r,(k<<1)|1);
}

這裏使用結構體來完成二叉樹的操做:spa

struct Tree
{
    int l,r,sum;
}tree[4*N];

最後,給出POJ2182的代碼,用以講解線段樹:code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=8888;
int ans[N],b[N];
int n;
struct Tree
{
    int l,r,sum;
}tree[4*N];
void build_tree(int l,int r,int k)     
{
    tree[k].l=l;
    tree[k].r=r;
    tree[k].sum=r-l+1;
    if(l==r) return ;
    int m=(l+r)>>1;
    build_tree(l,m,k<<1);
    build_tree(m+1,r,(k<<1)|1);
}
void solve(int num,int k,int i)
{
    tree[k].sum--;
    if(tree[k].l==tree[k].r)
    {
        ans[i]=tree[k].l;
        return ;
    }
    if(num<=tree[2*k].sum) solve(num,2*k,i);
    else solve(num-tree[2*k].sum,2*k+1,i);
}
int main()
{
    int i,j;
    scanf("%d",&n);
    for(i=2;i<=n;i++) scanf("%d",&b[i]);
    b[1]=0;
    build_tree(1,n,1);
    for(i=n;i>=1;i--) solve(b[i]+1,1,i);
    for(i=1;i<=n;i++) printf("%d\n",ans[i]);
}

部分知識來源:百度百科blog

相關文章
相關標籤/搜索