线段树区间合并操作(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; }
相关文章推荐
- HDU 3911 线段树区间合并+异或操作
- poj3667(线段树,区间合并)
- HDU 3911 Black And White(线段树区间合并+lazy操作)
- POJ3667(线段树区间合并)
- poj3667 线段树+区间合并
- HYSBZ 1858(Scoi2010) 序列操作(线段树+区间合并)
- poj3667(线段树区间合并)
- poj3667经典线段树合并操作
- [POJ3667]Hotel(线段树,区间合并)
- 线段树区间合并poj3667
- poj3667线段树区间合并
- HDU 3911 Black And White(线段树区间合并+lazy操作)
- poj3667(线段树区间合并&区间查询)
- poj3667 (线段树区间合并)
- 【bzoj1858】[Scoi2010]序列操作 线段树区间合并
- poj3667 Hotel (线段树+区间合并)
- 【线段树区间合并】POJ3667-Hotel
- poj3667(线段树区间合并)
- POJ3667:Hotel(线段树区间合并)
- poj3667 线段树区间合并