int getPoint(int l, int r, int max)
{
fp++;//新建一个空节点
f[fp].l = l;
f[fp].r = r;
f[fp].max = max;
return fp;//返回该节点的地址
}
int create(int l, int r)
{
int now = getPoint(l,r,-oo);//创建节点,记录节点地址
if (l<r)
{
f[now].lc = create(l, (l+r)/2);//建立左子树
f[now].rc = create((l+r)/2+1, r);//建立右子树
}
return now;
}
那么,有了创造,自然也要有修改。修改分为两种,区间修改和单个节点修改。单个节点修改很简单,从根节点开始递归,往下面扫描,发现要修改的节点,就修改它,并向上更新树的最优值(和),最后回到根节点。
代码如下:
void update(int root , int p, int x)
{
int l = f[root].l;
int r = f[root].r;
if ( p<l || r<p ) return ;//如果要修改的节点不在此区间内,停止递归,避免过多操作
if (l==r) { f[root].max=x; return ; }//如果这个区间刚好是要修改的叶节点,就修改它
update(f[root].lc, p, x);
update(f[root].rc, p, x);
f[root].max = max ( f [ f[root].lc ].max, f [ f[root].rc ].max );//更新当前节点
}
单个节点修改很简单,但是还有区间修改,一次更改整个区间,如果一个一个数进行单个修改,速度会比模拟还要慢,所以,这里就有一种更好的办法,使用lazy-tag的思想,就是给每一个要修改的节点标记一下,记录需要增加的数值,而不需要现在就把这个值加上去。当调用到这一个节点时,发现被标记过,就把增量加进去,并且给子节点也增加需要增加的数。
这里对于部分人来说有一个问题:修改某个区间,最优值就直接加上了增量。事实上,修改这一个区间,区间中每个数都相加同样的值,最大值依然不变,只比原来多了增量,就算增量是负数,也同样是这样。
事实上,更新单个节点也是区间更新中的一个特例,用区间更新同样能够操作,只不过左端点等于右端点。
代码如下:
void push(int root)//将当前节点的增量压入当前节点中
{
if (!f[root].s) return;
if (f[root].lc != 0)//增量传递给左儿子
{
f[f[root].lc].s = true;
f[f[root].lc].delta += f[root].delta;
}
if (f[root].rc != 0)//增量传递给右儿子
{
f[f[root].rc].s = true;
f[f[root].rc].delta += f[root].delta;
}
if (f[root].rc == 0 && f[root].lc == 0)
{
f[root].max += f[root].delta;
}
f[root].s = false;
f[root].delta = 0;//增加完当前节点,增量清空
}
void update(int root, int l, int r, int d)
{
push(root);
if (l==f[root].l && r==f[root].r)//区间刚好匹配
{
f[root].s = true;
f[root].delta += d;
return;//标记增量
}
int mid = (f[root].l+f[root].r) / 2;
if (l <= mid) update(f[root].lc,l,min(mid,r),d);//更新左子树
if (r >= mid+1) update(f[root].rc,max(mid+1,l),r,d);//更新右子树
}
有了创造,有了修改,可是没有查询,这一棵线段树也发挥不了多大作用。查询线段树,用到的一样是很巧妙的递归操作。查询一个区间的最优值,从根节点开始,向下递归,如果是查询的区间被一分为二,就查询左右儿子的区间,返回查询到的答案,合并两个答案,得到最优值。如果在左(右)区间,就直接递归向下找。最后返回的结果就是查询的区间的最优值。
代码如下:
注意,如果是区间修改需要在一开始将增量增加到节点中。即push(root);int query(int root,int l,int r)
{
if (l==f[root].l && r==f[root].r) return f[root].max;//区间完全匹配,返回当前区间最优值
int mid = (f[root].l+f[root].r) / 2;
int ans = -oo;
if (l <= mid) ans = max(ans,query(f[root].lc,l,min(mid,r)));//查找左儿子区间
if (r >= mid+1) ans = max(ans,query(f[root].rc,max(mid+1,l),r));//查找右儿子区间
return ans;//返回最优值
}