线段树就要比RMQ算法高级了,上午刚学完RMQ下午公关了线段树就来写一下自己的认知了。
对于线段树,就是一个由根结点往下出发的过程,把最最最上头的那个根结点定义为序号1,可以知道每个根结点(序号为rt、范围l~r)的左子树序号都为rt<<1(即rt*2)且他们的右子树节点都为rt<<1|1(即rt*2+1),然后左右子树平分根结点的长度,即左子树的范围为l~mid,右子树的范围为mid+1~r。
然后我上一下我的模版:
建树模版:
void build(int l,int r,int rt) //l、r指的是根的左右范围,rt指的是根的节点序号
{
if(l==r) mx[rt] = a[l]; //叶子结点,无根结点了
else
{
int mid = (l+r)>>1;
build(l,mid,rt<<1); //树的左子树的序号等于根*2
build(mid+1,r,rt<<1|1); //树的右子树的序号等于根*2+1
mx[rt] = max(mx[rt<<1],mx[rt<<1|1]); //得到值
}
}
查询模版:
int query(int l,int r,int rt,int ql,int qr) //返回ql~qr区间的最大值(l、r指的是原区间)
{
if(ql==l&&qr==r) return mx[rt]; //此时返回这一节点的值
else
{
int mid = l+r>>1;
if(qr<=mid) return query(l,mid,rt<<1,ql,qr); //不在范围,直接取另一条
else if(ql>mid) return query(mid+1,r,rt<<1|1,ql,qr); //不在范围,直接取另一头
else return max(query(l,mid,rt<<1,ql,mid),query(mid+1,r,rt<<1|1,mid+1,qr)); //范围之内,比较
}
}
这里有这样一个函数很容易被写错:
max(query(l,mid,rt<<1,ql,mid),query(mid+1,r,rt<<1|1,mid+1,qr));
记住qr和ql的更新!!!
我T过几次都是因为此处的qr和ql都忘记了要使用新的值,不然永远都找不到停止条件(ql==l && qr==r)。
更新点模版:
void update(int l,int r,int rt,int p,int v) //更新了某一节点,则需要重新赋值
{
if(l==r) mx[rt] = v; //叶子结点,直接赋值
else
{
int mid = l+r>>1;
if(p<=mid)update(l,mid,rt<<1,p,v);
else update(mid+1,r,rt<<1|1,p,v);
mx[rt] = max(mx[rt<<1],mx[rt<<1|1]);
}
}
还有一些需要注意的点:
譬如开了一个mx的数组记录树节点,我们需要把mx开的足够大,至少得保证不会RE!