由于网上的其它博客对初学者非常不友好,所以我就来写一篇博客帮助萌新们快速入门树套树啦。所谓线段树套线段树就是在第一棵线段树的每一个结点上再开一颗线段树,第一棵线段树用来维护第一维的数据,再用第一棵线段树上新开的线段树来维护第二维的数据。是不是没听懂呢,下面我们直接上图:
这就是树套树大概的模样,横线上面是第一棵线段树,下面是对第一棵线段树的每一个结点新开的线段树,就像是每一个点内都套着一棵线段树一样,那么二维线段树就可以用来维护平面信息下面看一道例题:
陌上花开
首先对第一维进行排序,然后用树套树进行处理,具体来讲,我们将第二维和第三维视为平面中的一点的坐标,然后每次单点修改,二维区间查询,就是统计一个矩形内部的元素之和,我们对第一棵线段树的每一个结点进行修改是实际上就是对这个结点中套着的线段树进行修改,对套着的线段树进行修改是才是和一维线段树一样的修改方法,另外,树套树由于占用空间过大,需要使用动态开点的方法,话不多说,上代码:
#include
#include
#include
#include
#define maxn 20000005
using namespace std;
int read()
{
int x=1,res=0;
char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-')
x=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
res=res*10+(c-'0');
c=getchar();
}
return res*x;
}
struct node{
int a,b,c;
}g[maxn];
struct tree{
int l,r;
}t1[maxn],t2[maxn];
int n,p,ans[maxn],tot1,tot2,root1,root[maxn],d[maxn],last;
bool cmp(node a,node b) {return a.a<b.a;}
void _2nd_modify(int &k,int l,int r,int i)
{
if(!k) k=++tot2;
d[k]+=1;
if(l==r) return;
int mid=(l+r)>>1;
if(mid>=g[i].c) _2nd_modify(t2[k].l,l,mid,i);
else _2nd_modify(t2[k].r,mid+1,r,i);
}
void _1st_modify(int &k,int l,int r,int i)
{
if(!k) k=++tot1;
_2nd_modify(root[k],1,p,i);
if(l==r) return;
int mid=(l+r)>>1;
if(mid>=g[i].b) _1st_modify(t1[k].l,l,mid,i);
else _1st_modify(t1[k].r,mid+1,r,i);
}
int _2nd_query(int k,int l,int r,int x,int y,int i)
{
if(k==0) return 0;
if(x<=l&&r<=y) return d[k];
int mid=(l+r)>>1,res=0;
if(x<=mid) res+=_2nd_query(t2[k].l,l,mid,x,y,i);
if(mid+1<=y) res+=_2nd_query(t2[k].r,mid+1,r,x,y,i);
return res;
}
int _1st_query(int k,int l,int r,int x,int y,int i)
{
if(k==0) return 0;
if(x<=l&&r<=y) return _2nd_query(root[k],1,p,1,g[i].c,i);
int mid=(l+r)>>1,res=0;
if(x<=mid) res+=_1st_query(t1[k].l,l,mid,x,y,i);
if(mid+1<=y) res+=_1st_query(t1[k].r,mid+1,r,x,y,i);
return res;
}
int main()
{
n=read();p=read();
for(int i=1;i<=n;i++)
{
g[i].a=read();
g[i].b=read();
g[i].c=read();
}
sort(g+1,g+1+n,cmp);last=1;
for(int i=1;i<=n;i++)
{
_1st_modify(root1,1,p,i);
if(g[i+1].a!=g[i].a)
{
for(int j=last;j<=i;j++)
ans[_1st_query(root1,1,p,1,g[j].b,j)-1]++;
last=i+1;
}
}
for(int i=0;i<n;i++)
printf("%d\n",ans[i]);
}
再看一道题:
GDCPC 2021 K ~~ Kera’s line segment
还是将问题转换为平面问题,然后用二维线段树维护平面最大和最小值即可。
#include
#include
#include
#include
#define maxn 20000005
using namespace std;
int read()
{
int x=1,res=0;
char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-')
x=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
res=res*10+(c-'0');
c=getchar();
}
return res*x;
}
struct nn{
int Max,Min;
};
struct node{
int a,b,c;
}g[maxn];
struct tree{
int l,r,Min,Max;
}t1[maxn],t2[maxn];
int n,tot1,tot2,root1,root[maxn],last,op,m,aa,bb,cc;
void _2nd_modify(int &k,int l,int r,int i)
{
if(!k) k=++tot2;
t2[k].Max=max(t2[k].Max,g[i].c);
t2[k].Min=min(t2[k].Min,g[i].c);
if(l==r) return;
int mid=(l+r)>>1;
if(mid>=g[i].b) _2nd_modify(t2[k].l,l,mid,i);
else _2nd_modify(t2[k].r,mid+1,r,i);
}
void _1st_modify(int &k,int l,int r,int i)
{
if(!k) k=++tot1;
_2nd_modify(root[k],1,3000,i);
if(l==r) return;
int mid=(l+r)>>1;
if(mid>=g[i].a) _1st_modify(t1[k].l,l,mid,i);
else _1st_modify(t1[k].r,mid+1,r,i);
}
nn _2nd_query(int k,int l,int r,int x,int y)
{
if(k==0) return (nn){0,(int)2e9};
if(x<=l&&r<=y) return (nn){t2[k].Max,t2[k].Min};
int mid=(l+r)>>1,res1=0,res2=2e9;
if(x<=mid)
{
res1=max(res1,_2nd_query(t2[k].l,l,mid,x,y).Max);
res2=min(res2,_2nd_query(t2[k].l,l,mid,x,y).Min);
}
if(mid+1<=y)
{
res1=max(res1,_2nd_query(t2[k].r,mid+1,r,x,y).Max);
res2=min(res2,_2nd_query(t2[k].r,mid+1,r,x,y).Min);
}
return (nn){res1,res2};
}
nn _1st_query(int k,int l,int r,int x,int y)
{
if(k==0) return (nn){0,(int)2e9};
if(x<=l&&r<=y) return _2nd_query(root[k],1,3000,x,y);
int mid=(l+r)>>1,res1=0,res2=2e9;
if(x<=mid)
{
res1=max(res1,_1st_query(t1[k].l,l,mid,x,y).Max);
res2=min(res2,_1st_query(t1[k].l,l,mid,x,y).Min);
}
if(mid+1<=y)
{
res1=max(res1,_1st_query(t1[k].r,mid+1,r,x,y).Max);
res2=min(res2,_1st_query(t1[k].r,mid+1,r,x,y).Min);
}
return (nn){res1,res2};
}
int main()
{
n=read();m=read();
for(int i=1;i<=1e7;i++)
t2[i].Min=2e9;
for(int i=1;i<=n;i++)
{
g[i].a=read();g[i].b=read();g[i].c=read();
_1st_modify(root1,1,3000,i);
}
last=0;
for(int i=1;i<=m;i++)
{
op=read();
if(op==1)
{
aa=read();bb=read();cc=read();
aa=aa^last;bb=bb^last;
g[i].a=aa;g[i].b=bb;g[i].c=cc;
_1st_modify(root1,1,3000,i);
}
if(op==2)
{
aa=read();bb=read();
// printf("%d %d\n",aa,bb);
aa=aa^last;bb=bb^last;
nn u=_1st_query(root1,1,3000,aa,bb);
// printf("max:%d min:%d\n",u.Max,u.Min);
if(u.Min==2e9) u.Min=0;
last=u.Max-u.Min;
printf("%d\n",last);
}
}
return 0;
}