线段树总结
今天学习线段树,有一些感悟。
第一,要想清楚pushdown和pushup的用处:
一个是将u的信息传递给子节点,一个是将子节点的信息传递给父亲节点。
第二,建树的判定:
当left和right相等时,赋值成输入的a[u],然后return,完成部分的建树。
第三,更新时要分3种情况:
其一是所要更新的区间全部在s[u].l到mid时,直接在L(u)继续向下找,这里要注意,所找的区间还是left到right,跟mid无关。
其二是所要更新的区间全部在mid到s[u].r时,在R(u)继续向下找,这里也要注意,所找的区间还是left到right。
其三是所要更新的区间在两边都有交集时,要分两边找,这里要注意:所找的区间就是left到mid和mid+1到right了。千万不能写错!(调了很久才发现。⊙﹏⊙b汗)
第四,结构体:
结构体内定义left,right表示s[u]区间的两端,其他变量由题意定义。(例如sum,height,add,price等)结构体外的数组一般定成4*MAXN那么大就可以了。
第五:格式:
一定要看清题目的输入格式和输出格式!有些题输入是多组数据,还有输出时一般情况要换行。
下面是线段树的一些基本框架:
1.建树:
void Build (int u,int left,int right)//u表当前结点
{ //left right 表左区间范围[left,right]
node[u].l = left,node[u].r = right;
….. //结点信息的初始化
node[u].l = left,node[u].r = right;
….. //结点信息的初始化
if (node[u].l == node[u].r) //到叶结点 return
{
…… //某些赋值
{
…… //某些赋值
return ;
}
int mid = (node[u].l + node[u].r)>>1;
Build (L(u),left,mid);
Build (R(u),mid+1,right);
}
}
int mid = (node[u].l + node[u].r)>>1;
Build (L(u),left,mid);
Build (R(u),mid+1,right);
}
2.更新:
void Update(int u,int left,int right,data val)
{
if (left<=node[u].l&&node[u].r <= right){
…….. //进行某些更改操作
{
if (left<=node[u].l&&node[u].r <= right){
…….. //进行某些更改操作
return ;
}
Pushdown(u); // 成段更新时,这里一般都有个延时更新
int mid = (node[u].l + node[u].r)>>1; //然后就是往左 往右 或左右找区间 几乎所有的
if (right <= mid) Update(L(u),left,right,val); //线段树都这样
else if (left > mid) Update(R(u),left,right,val);
else {
Update(L(u),left,mid,val);
Update(R(u),mid+1,right,val);
}
Pushup(u); //这里也一般有个向上更新
}
Pushdown(u); // 成段更新时,这里一般都有个延时更新
int mid = (node[u].l + node[u].r)>>1; //然后就是往左 往右 或左右找区间 几乎所有的
if (right <= mid) Update(L(u),left,right,val); //线段树都这样
else if (left > mid) Update(R(u),left,right,val);
else {
Update(L(u),left,mid,val);
Update(R(u),mid+1,right,val);
}
Pushup(u); //这里也一般有个向上更新
}
3.查询:
int Query(int u,int left,int right)
{
if (left <= node[u].l&&node[u].r <= right)
return node[u].sum;
Pushdown(u); //同update操作 视情况可有可无
int mid = (node[u].l + node[u].r)>>1;
if (right <= mid) return Query(L(u),left,right);
else if (left > mid) return Query(R(u),left,right);
else return (Query(L(u),left,mid) + Query(R(u),mid+1,right));
Pushup(u); //
}
{
if (left <= node[u].l&&node[u].r <= right)
return node[u].sum;
Pushdown(u); //同update操作 视情况可有可无
int mid = (node[u].l + node[u].r)>>1;
if (right <= mid) return Query(L(u),left,right);
else if (left > mid) return Query(R(u),left,right);
else return (Query(L(u),left,mid) + Query(R(u),mid+1,right));
Pushup(u); //
}
4.向上回溯:
void Pushup(int u)
{
node[u].sum = node[L(u)].sum + node[R(u)].sum;
{
node[u].sum = node[L(u)].sum + node[R(u)].sum;
…….
…….
return ;
}
return ;
}
5.延时更新(向下更改):
void Pushdown (int u) //延迟覆盖操作
{
if (node[u].state == -1) {
更新父结点,左右子结点信息
}
else if (node[u].state == 1){
更新父结点,左右子结点信息
{
if (node[u].state == -1) {
更新父结点,左右子结点信息
}
else if (node[u].state == 1){
更新父结点,左右子结点信息
}
else {
………
}
}
}