线段树
Posted maoruimas
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树相关的知识,希望对你有一定的参考价值。
动态区间最值问题(查询、更新)
线段树原理简单,但我看刘书上实现代码比较麻烦,于是试着自己实现了一下。说明如下:
- 出于简化的目的,总是将线段总长视为2的方幂(如果不足,预先补齐。且这样不会本质上影响复杂度)
- 建树:由于是完全二叉树,叶子结点的编号是连续的,建树时只要从底向上扫一遍即可。复杂度(O(n))
- 更新:从底向上扫一遍。复杂度(O(log(n)))
查询:按照线段树的思想,找到待查询区间内完全二叉树的树根,并取最小值即可。复杂度的为(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));
}
}
以上是关于线段树的主要内容,如果未能解决你的问题,请参考以下文章