标签:struct 区间更新 难度 完全 结构 nod ace 程序 out
这道题目在做的时候是一道让我比较头疼的题目,记得那时候总是整理完思路而不想写程序,很多次刚开始写觉得麻烦就又放弃了。不过那天下午还是逼着自己怎么也要把这题做了。
题目要求长度大于k的最靠左的一段连续空白。
线段树应该是人人都能想到的解决这道题的数据结构。
接下来我们应当考虑怎么分,这么合。
先考虑分。(假设合法解存在)
查询函数。
1:左区间的最大连续空白大于等 k ,递归到左子树。
2:左区间靠右一部分与右区间靠左一部分的连续空白满足条件,直接对该区间进行长度为k的覆盖。
3:递归到右子树进行。
按照上述方式,每次覆盖操作一定在步骤二进行,也就是说,我将步骤二视为递归边界,且查询到合法区间后,我接着用另一个函数进行覆盖操作。
覆盖函数。
就像普通的区间覆盖一样,因为只要找到了正确的区间,覆盖操作的难度就跟删除操作一样了,打上标记就OK了。
这道题目的另一个难点在于合。
为了正确的得到最大连续空白,我在线段树节点中保存了如下变量:
l:区间左端点。
r:区间右端点。
left: 区间由左端点向右延伸的最大空白。
right:区间由右端点向左延伸的最大空白。
fix:左子树right+右子树left的值。
maxx:区间最大空白。
lazy:区间是否被完全覆盖(1)或完全空白(2)。
有了这些变量,只要写好update(),这道题就会顺利解决了。
其实也并不难写。
l,r 在建树时直接得到且不会改变。
left:由左子区间的 left 值更新,且在此要考虑左右区间合并的问题,如果 lc->l + lc->left == rc->l ,也就是说左子区间完全空白,那么当前区间的 left 要加上右子区间的 left,才能得到正确的值。
right: 由右子区间的 right 值更新,同样考虑区间合并,如果 rc->r - rc->right == lc->r ,说明右子区间完全空白,那么当前区间的 right 要加上左子区间的 right,这两个操作是对称的。
fix:这个简单,直接 lc->right + rc->left ,即可。
maxx:取左子区间 maxx,右子区间maxx,当前区间 fix 三者中较大的一个。
lazy:覆盖时打标记1,清空时打标记2。
update()顺利完成,就是有些繁琐但是很好理解。
接下来只剩一个pushdown()。
因为pushdown()在区间完全覆盖或完全空白时下传调用,因此很好写,只需要把子区间更新为完全覆盖或完全空白并打上标记,清空自己的标记即可,常规操作。
至此,这道题就顺利解决了。
PS:写题的时候没注意,不过写题解时我发现我的覆盖操作复杂度好像是 O ( log2 ) 的,因为我先查询对应区间后进行覆盖。
// q.c #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; const int M=50000+10; int n,m; struct Node { int l,r,left,right,fix,maxx,lazy; Node():l(0),r(-1),left(0),right(0),fix(0),maxx(0),lazy(0) {} }; struct SegmentTree { int root; Node nd[M<<2]; SegmentTree():root(1) {} void update(int o) { Node &p=nd[o],&lc=nd[o<<1],&rc=nd[o<<1^1]; p.left=lc.left; if(lc.l+lc.left==rc.l) p.left+=rc.left; p.right=rc.right; if(rc.r-rc.right==lc.r) p.right+=lc.right; p.fix=lc.right+rc.left; p.maxx=max(lc.maxx,rc.maxx); p.maxx=max(p.maxx,p.fix); } void pushdown(int o) { Node &p=nd[o],&lc=nd[o<<1],&rc=nd[o<<1^1]; if(p.lazy==1) { lc.left=lc.right=lc.fix=lc.maxx=0; rc.left=rc.right=rc.fix=rc.maxx=0; lc.lazy=rc.lazy=1; p.lazy=0; } else if(p.lazy==2) { lc.left=lc.right=lc.fix=lc.maxx=lc.r-lc.l+1; rc.left=rc.right=rc.fix=rc.maxx=rc.r-rc.l+1; lc.lazy=rc.lazy=2; p.lazy=0; } } void build(int o,int l,int r) { nd[o].l=l,nd[o].r=r; if(l==r) nd[o].left=nd[o].right=nd[o].fix=nd[o].maxx=1; else { int mid=(l+r)>>1; build(o<<1,l,mid); build(o<<1^1,mid+1,r); update(o); } } void solve(int o,int l,int r,int x) { Node &p=nd[o]; if(l<=p.l&&p.r<=r) { if(x) p.left=p.right=p.fix=p.maxx=0,p.lazy=1; else p.left=p.right=p.fix=p.maxx=p.r-p.l+1,p.lazy=2; } else { pushdown(o); int mid=(p.l+p.r)>>1; if(l<=mid) solve(o<<1,l,r,x); if(r>mid) solve(o<<1^1,l,r,x); update(o); } } int find(int o,int x) { Node &p=nd[o],&lc=nd[o<<1],&rc=nd[o<<1^1]; if(p.maxx<x) return 0; pushdown(o); if(lc.maxx>=x) return find(o<<1,x); if(p.fix>=x) { if(p.l==p.r) { solve(1,p.l,p.r,1); return p.l; } int ans=lc.right; solve(1,lc.r-ans+1,lc.r,1); solve(1,rc.l,rc.l+x-ans-1,1); return lc.r-ans+1; } return find(o<<1^1,x); } }t; int main() { freopen("haoi13t4.in","r",stdin); freopen("haoi13t4.out","w",stdout); scanf("%d%d",&n,&m); t.build(1,1,n); int opt,a,b; for(int i=1;i<=m;i++) { scanf("%d",&opt); if(opt==1) scanf("%d",&a),printf("%d\n",t.find(t.root,a)); else scanf("%d%d",&a,&b),t.solve(t.root,a,a+b-1,0); } return 0; }
标签:struct 区间更新 难度 完全 结构 nod ace 程序 out
原文地址:https://www.cnblogs.com/qjs12/p/8849074.html