您的位置:首页 > 其它

线段树区间合并操作(POJ3667)

2020-07-09 10:10 357 查看

线段树的区间合并操作

  • 线段树的区间合并操作维护信息时,除了区间和、更新标记等,最重要的就是要维护lsum和rsum这两个特殊值
  • 这两个值分别各自的意义是:lsum 代表以该区间左端点为起点的连续段的长度(左连续段) ,rsum代表以该区间右端点为终点(注意是终点)的连续段的长度(右连续段),而rsum[rt<<|1]+lsum[rt<<1|1]则是代表了含有当前区间终点的最大连续段长度。
  • 另外还有一个需要维护的值就是 sum[rt] 也就是当前节点下的最大连续区间的长度,而对于每一次lsum和rsum更新完成后 sum[rt]应这样给出:
  • sum[rt] = max(rsum[rt<<1]+lsum[rt<<1|1],max(sum[rt<<1],sum[rt<<1|1])
  • 其值就在左子树最大连续区间,右子树最大连续区间和中间连续段中取最大值
  • 另外 区间合并在线段树操作中主要体现在pushup函数的变化上:
void pushup(int rt,int tot)
{
if(tot == 1) return ;
lsum[rt] = lsum[rt<<1];
rsum[rt] = rsum[rt<<1|1];
// 体现区间合并的地方
if(lsum[rt] == (tot-tot/2)) lsum[rt]+=lsum[rt<<1|1];//代表左子树的区间满了需要加上右子树左区间
if(rsum[rt] == (tot/2)) rsum[rt]+=rsum[rt<<1];// 和上方同理
sum[rt] = max(rsum[rt<<1]+lsum[rt<<1|1],max(sum[rt<<1],sum[rt<<1|1]));
}

POJ3667 HDU4553(比前面麻烦一点 维护两个树)
这两道题都要求返回一个查询的位置信息写在query函数中就是

int query(int rt,int l,int r,int len)// len代表需要满足的区间长度
{
if(l == r) return l;
pushdown(rt,r-l+1);
int mid = (l+r)>>1;
if(sum[rt<<1] >= len) return query(rt<<1,l,mid,len);//做子树区间满足要求 那么位置一定在左子树区间中
else if(rsum[rt<<1]+lsum[rt<<1|1] >= len) return mid-rsum[rt<<1]+1;// 中间段满足要求 那么起始点一点是左子树右连续区间的起点
else return query(rt<<1|1,mid+1,r,len);//其他情况就一定存在于右子树中
}

代码:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 5e4+7;
int sum[MAXN<<2],lsum[MAXN<<2],rsum[MAXN<<2];
int lazy[MAXN<<2];
// 这个就是 hdu4553的弱化版
// 这中区间一个起始位置的题目应该都是一个套路
// 区间合并的关键 在pushup中 而query或者其他函数需要根据具体题目具体 确定
void pushup(int rt,int tot)
{
if(tot == 1) return ;
lsum[rt] = lsum[rt<<1];
rsum[rt] = rsum[rt<<1|1];
if(lsum[rt] == (tot-tot/2)) lsum[rt]+=lsum[rt<<1|1];
if(rsum[rt] == (tot/2)) rsum[rt]+=rsum[rt<<1];
sum[rt] = max(rsum[rt<<1]+lsum[rt<<1|1],max(sum[rt<<1],sum[rt<<1|1]));
}

void pushdown(int rt,int tot)
{
if(tot == 1) return ;
if(lazy[rt]){
lazy[rt<<1] = lazy[rt<<1|1] = lazy[rt];
if(lazy[rt] == 2){
sum[rt<<1] = lsum[rt<<1] = rsum[rt<<1] = tot-tot/2;
sum[rt<<1|1] = lsum[rt<<1|1] = rsum[rt<<1|1] = tot/2;
}
else if(lazy[rt] == 1){
sum[rt<<1] = lsum[rt<<1] = rsum[rt<<1] = 0;
sum[rt<<1|1] = lsum[rt<<1|1] = rsum[rt<<1|1] = 0;
}
lazy[rt] = 0;
}
}

void build(int rt,int l,int r)
{
lazy[rt] = 0;
if(l == r){
sum[rt] = lsum[rt] = rsum[rt] = 1;
return ;
}
//pushdown(rt,r-l+1);
int mid = (l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
pushup(rt,r-l+1);
}

void update(int rt,int l,int r,int ul,int ur,int p)
{
if(l>=ul&&r<=ur){
if(p == 1){
sum[rt] = lsum[rt] = rsum[rt] = 0;
}
else if(p == 2){
sum[rt] = lsum[rt] = rsum[rt] = r-l+1;
}
lazy[rt] = p;
return ;
}
pushdown(rt,r-l+1);
int mid = (l+r)>>1;
if(ul <= mid) update(rt<<1,l,mid,ul,ur,p);
if(ur > mid) update(rt<<1|1,mid+1,r,ul,ur,p);
pushup(rt,r-l+1);
}

int query(int rt,int l,int r,int len)
{
if(l == r) return l;
pushdown(rt,r-l+1);
int mid = (l+r)>>1;
if(sum[rt<<1] >= len) return query(rt<<1,l,mid,len);
else if(rsum[rt<<1]+lsum[rt<<1|1] >= len) return mid-rsum[rt<<1]+1;
else return query(rt<<1|1,mid+1,r,len);
}

int main()
{
int n,m;
scanf("%d%d",&n,&m);
build(1,1,n);
int k;
while(m--){
scanf("%d",&k);
if(k == 1){
int len;
scanf("%d",&len);
if(sum[1]>=len){
int pos = query(1,1,n,len);
printf("%d\n",pos);
update(1,1,n,pos,pos+len-1,1);
}
else printf("0\n");
}
else if(k == 2){
int st,len;
scanf("%d%d",&st,&len);
update(1,1,n,st,st+len-1,2);
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: