线段树

动态区间最值问题(查询、更新)
线段树原理简单,但我看刘书上实现代码比较麻烦,于是试着自己实现了一下。说明如下:

  1. 出于简化的目的,总是将线段总长视为2的方幂(如果不足,预先补齐。且这样不会本质上影响复杂度)
  2. 建树:由于是完全二叉树,叶子结点的编号是连续的,建树时只要从底向上扫一遍即可。复杂度(O(n))
  3. 更新:从底向上扫一遍。复杂度(O(log(n)))
  4. 查询:按照线段树的思想,找到待查询区间内完全二叉树的树根,并取最小值即可。复杂度的为(O(log(n)))

查询复杂度的证明:线段树的查询复杂度应当时(O(log(n)))。由代码可知,可能会使复杂度增加的仅有while循环。但能够进入while,当且仅当此时的l和r在同一颗子树里。分析可知,下一次进入while的树高小于等于上一次出while的树高,因此while循环最多要花(O(log(n)))的时间。

int stree[int i]stree[i]表示结点i的值
n补齐后的线段总长
int to_id(int x)原数据编号到结点编号
int query(int l,int r)查询[l,r]区间最值
void update(int p,int v)修改第p个数据为v

const int inf=0x3f3f3f3f;
int stree[4000];
int n;
inline int to_id(int x){return x+n-1;}
int query(int l,int r)
{
    int ans=inf;
    l=to_id(l);r=to_id(r);
    for(int t=l&(-l);l<=r;l=l+t)
    {
        while(l+t-1>r)t/=2;
        ans=min(ans,stree[l/t]);
    }
    return ans;
}
void update(int p,int v)
{
    stree[p=to_id(p)]=v;
    while(p=p/2)stree[p]=min(stree[2*p],stree[2*p+1]);
}

int main()
{
    ...
    int nn;
    while(~scanf("%d",&nn))
    {
	    //补齐n
        n=1;
        while(n<nn)n*=2;
        //建树
        for(int i=n;i<2*n;++i)
        {
            int tmp=inf;
            if(i-n<nn)scanf("%d",&tmp);
            stree[i]=tmp;
        }
        for(int i=n-1;i>0;--i)stree[i]=min(stree[2*i],stree[2*i+1]);
        //测试
        int l,r;
        while(~scanf("%d%d",&l,&r))printf("%d
",query(l,r));
    }
}
原文地址:https://www.cnblogs.com/maoruimas/p/9739008.html