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

相关文章
相关标签/搜索