顾名思义,二维线段树就是在一棵线段树的每一个节点,都保存着另一棵线段树的根节点编号。
二维线段树通常支持以下2种功能:
1、单点修改;
2、二维区间查询。
为了实现这两种功能,我们需要建一棵外层线段树(可以动态开点也可以静态开点),对于外层线段树的每一个节点,我们都保存一个内层线段树的根节点编号(内层线段树必须动态开点,否则MLE)。
代码:
#define sz 100000
struct hh
{
long long lson,rson,rt;//左儿子,右儿子,内层线段树根节点编号
}out[sz<<1];//外层线段树
struct hhh
{
long long lson,rson,v;//左儿子,右儿子,值
}in[sz<<7];//内层线段树
在单点修改时,要在外层线段树查询点的一维,并沿途修改内层线段树。内层线段树查询点的另一维,找到后修改并回溯。
代码:
#define sz 100000
void modify_in(int &k,int l,int r,int y,int v)//内层修改
{
if (!k) k=++cnt2;//动态开点
in[k].v=max(in[k].v,v);//更新节点值
if (l==r) return;//修改完毕
int mid=(l+r)>>1;
if (y<=mid) modify_in(in[k].lson,l,mid,y,v);
else modify_in(in[k].rson,mid+1,r,y,v);
}
void modify_out(int &k,int l,int r,int x,int y,int v)//外层修改
{
if (!k) k=++cnt1;//动态开点
modify_in(out[k].rt,1,sz,y,v);//沿途修改
if (l==r) return;//修改完毕
int mid=(l+r)>>1;
if (x<=mid) modify_out(out[k].lson,l,mid,x,y,v);
else modify_out(out[k].rson,mid+1,r,x,y,v);
}
二维区间查询时,与普通线段树很像,但要在锁定一维后在内层线段树查找另一维,然后才能得出答案。
代码:
#define sz 100000
int query_in(int k,int l,int r,int y1,int y2)//内层查询
{
if (!k) return 0;//没有这个点
if (y1<=l&&r<=y2) return in[k].v;//第二维包含,返回值
int mid=(l+r)>>1,ret=0;
if (y1<=mid) ret=max(ret,query_in(in[k].lson,l,mid,y1,y2));
if (y2>mid) ret=max(ret,query_in(in[k].rson,mid+1,r,y1,y2));
return ret;
}
int query_out(int k,int l,int r,int x1,int x2,int y1,int y2)
{
if (!k) return 0;//没有这个点
if (x1<=l&&r<=x2) return query_in(out[k].rt,1,sz,y1,y2);//第一维包含,查询第二维
int mid=(l+r)>>1,ret=0;
if (x1<=mid) ret=max(ret,query_out(out[k].lson,l,mid,x1,x2,y1,y2));
if (x2>mid) ret=max(ret,query_out(out[k].rson,mid+1,r,x1,x2,y1,y2));
return ret;
}
这便是二维线段树的全部实现方法。
实战:
1、POJ2029 Get Many Persimmon Trees http://poj.org/problem?id=2029
2、POJ1195 Mobile phones http://poj.org/problem?id=1195
3、洛谷4093 序列 https://www.luogu.org/problemnew/show/P4093
(洛谷4093:我的另一篇博客内给出了题解http://blog.csdn.net/pb122401/article/details/79310796)