线段树,作为一项统计神学,和树状数组一样也是一项需要掌握的杀手锏。
线段树的代码分为三个模块:建树·更改·查询。
1.建树:
建树的过程是通过不断的分成左子树和右子树,一层一层的叠上去。采用的是递归的思想。
void Build(int left,int right,int rt)
{
if(left == right)
{
scanf("%d",&tree[rt]);
return;
}
int mid = (left+right)>>1;
Build(left,mid,rt<<1);
Build(mid+1,right,(rt<<1)+1);
tree[rt]=max(tree[rt<<1],tree[(rt<<1)+1]);
}
2.更改:
更改的过程是从树的最下面改的,所以在改了最下面后,我们还要一层一层的叠上去。
void Update(int p,int q,int left,int right,int rt)
{
if(left == right)
{
tree[rt]=q;
return;
}
int mid = (left+right)>>1;
if(p <= mid)
Update(p,q,left,mid,rt<<1);
else
Update(p,q,mid+1,right,(rt<<1)+1);
tree[rt] = max(tree[rt<<1],tree[(rt<<1)+1]);
}
3.查询:
查询才是整个线段树的核心,是成就此神学统计方法的绝学^^~,通过判断线段的左右在当前查询的线段中的位置来进行查询。
int Query(int L,int R,int left,int right,int rt)
{
if(L<=left && right<=R)
return tree[rt];
int mid = (right+left)>>1;
int ret=0;
if(L<=mid)
ret=max(ret,Query(L,R,left,mid,rt<<1));
if(R>mid)
ret=max(ret,Query(L,R,mid+1,right,(rt<<1)+1));
return ret;
}
查询功能还可以查询一段里面的所有子树的和
int Query(int left,int right,int i)//查询
{
int mid;
if(b[i].left==left && b[i].right==right) return b[i].sum;
mid = (b[i].left+b[i].right)/2;
if(right <= mid) return Query(left,right,2*i);//在左子树
else if(left > mid) return Query(left,right,2*i+1);//在右子树
else return Query(left,mid,2*i)+Query(mid+1,right,2*i+1);//否则在中间
}