[线段树][扫描线][面积交] HDU 1255

这次写了这个题目之后我发现之前对面积并的那个cal计数的意义还是理解还是有一点偏差:这个东西表示的是当前节点对应的线段整体覆盖的情况。为0时没有被全覆盖(注意不等于没有被覆盖),为1时被全覆盖了1次(意为min(l,r)==1,不代表不大于1),为k时表示min(l,r)==k。而且对应的只是一个节点对应的整个线段,跟只表示当前整段线段的状况,儿子是什么样不知道。。。

这个题也貌似有很多种方法,我参考了一位前辈的博客之后用了Len1表示全覆盖次数≥1的长度,Len2表示全覆盖次数≥2的长度,当然全部都是针对当前节点。Len1的维护方法和面积并的方法完全一样。然后Len2维护的时候就是,如果cnt[o]大于等于2,那么说明当前节点被全覆盖了两次,直接Len2=Len1,如果为1,说明当前节点被全覆盖了一次,那么只需要加上子节点中被全覆盖了一次的Len1就可以了(这样相当于在双亲节点里覆盖了两次),如果是叶子节点,没有孩子,那就直接是0,如果cnt为0,那就只有孩子里面的被全覆盖了两次之和才能更新当前点了。
这里还有一个需要注意的是,如果cnt为0,那么要么说明,当前节点从来没有被计算过,要么说明只计算过子节点,所以不会出现错误。
继续阅读“[线段树][扫描线][面积交] HDU 1255”

[比赛][线段树][模拟] 2018南京网络赛 G Lpl and Energy-saving Lamps

这题真的好鸡儿简单。。。差不多是除了第一题之外最简单的了。比赛的时候没看这个,iSika说很难就没做,唉

题意:给n个房间,当还有房间的灯没有被换成节能灯的时候每个月开始会买入m个节能灯,然后从左向右在列表中找小于m的房间换掉,如果当月的节能灯没有用完的话可以留给下个月。然后给q个询问问d第几个月结束的时候有多少个房间被换成了节能灯,以及手里剩下了多少节能灯。

解法:用value表示当前总共有多少个节能灯,把q个询问都读进来排序后从小到大操作(就是从第一个月开始模拟)。每次都找列表中<=value的最左边的值,找的方法是用线段树维护区间minv 信息,如果左子树minv<=value的话就向左走否则向右,如果小于minv[1]就不找,直接把当月的算出来。找到的话,令找到的房间的计数器+1,value-=找到的值,然后更新线段树这个点的minv为INF就行了。由于每个节点最多被修改一次,修改一次的代价为logn,所以总共的代价为qlogn,case closed! 继续阅读“[比赛][线段树][模拟] 2018南京网络赛 G Lpl and Energy-saving Lamps”

[线段树][扫描线][面积并] HDU1542 Atlantis

扫描线第一题,求矩形面积的并。思路很好理解,但是实现上有很多的问题。

1 维护的变量ala[o]代表含义是“当前节点对应区间全覆盖的点数”,举个例子来说,现在的区间各个节点的值如果是2 3 3 3 5 5 2,那么ala就应该是2。这个变量这么做的好处就是,当ala不为0的时候一定可以直接把整个区间的Len算出来:就是x[r]-x[l],当他为0的时候说明中间有的点为0,那么我们再考虑左右儿子Len之和。这个东西注意是不要pushdown的,如果pushdown了反而会使得问题变得复杂,因为如果你只是往里面+1也要pushdown的话,那等于原来的可以直接得到的Len现在反而要往下走才可以得到。那么不pushdown会不会出现重复加的情况?也不会,因为这个问题里所有的形状都是矩形,有一个上下边使得ala[o]+1,则一定有另外一个边使得他-1,而这中间的呢?因为肯定这一条线段都是还是属于计算范围的,所以也没有多加,一定是对的,这个特点很重要,今早赖床的时候想了下才明白其中的奥妙所在。。。。

引用一位dalao的话描述一下

这里的区间覆盖不需要打懒惰标记。为什么呢?因为很显然,每个下边都有与之对应的上边,它显然不会被比它的长度小的上边给清除。而如果有比它长的上边,也一定有和那个上边对应的下边还存在。所以用那个上边清除下边的时候,一定会先递归到那个更长的下边,还没有递归到它就停止递归了。所以每个上下边是一一对应的,无需下传标记等。

2 这个线段树不是[l,r],而是[l,r),因为我们这里离散化之后,如果按照闭区间来算,那[mid,mid+1]这一段就不能被计算到了,所以要用左闭右开,这样的话对应的一开始的区间右边要+1,而这样又会有问题就是原来的整个线段,也就是o==1时候的线段,以及所有的右侧端点为整个线段树右端点的线段,他们的右侧怎么处理,这可是个必须要左闭右闭的啊?我这里用的是在xs离散化数组的后面加上一个和xs[cnt1]相等的元素,这样就可以不用特殊判断了。

继续阅读“[线段树][扫描线][面积并] HDU1542 Atlantis”

[线段树][区间最值操作][论文题][难] HDU5306 Gorgeous Sequence

这个题也是看了一晚上,各种题解各种论文各种ppt emmmmmmm

话说发现真的线段树这东西不能看别人的代码,怎么看怎么看不懂,非得自己写一遍才是。感觉市面上常见的线段树也就两种写法,一种是pushdown以及在update预判返回的时候把维护子节点的事情做了,维护只维护当前节点所以又叫pushup,另一种是pushdown和预判都只改变标记,而所有的维护都交给maintain来做,(⊙﹏⊙)我一直写的是后一种,但是这里写前一种貌似比较方便(🌫)因为update在这里兼具了更新和搜索的功能,所以最好是在pushdown的时候就把维护子节点的事情做了。(感觉自己对pushdown标记的理解之前还是不到位啊emmmm还是得写)
继续阅读“[线段树][区间最值操作][论文题][难] HDU5306 Gorgeous Sequence”

[线段树][区间等比/等差数列修改] CodeForces – 446C DZY Loves Fibonacci Numbers

这个题的官方题解简直玄学,根号5取模,然后转化成等比数列,服气。

还好评论区有人给了更简便的做法:广义斐波那契数列。任意两个广义斐波那契数列相加仍然是斐波那契数列,而且只要知道了前两项的和就可以知道任意一项的和和任意一项的开始的前缀和。比如若第一二项为a,b,那么手推几下就可以发现,fab[n]=af[n-2]+bf[n-1],然后前缀和是fsum[n]=f[n+2]-f[2](这个也很容易推出来,甚至奇数项偶数项分别的和都能推出来),然后这时候就很简单了,对于每一个区间只需要保存其左端点处的a,b就可以了,把ab做成lazy tag就可以实现区间修改了。注意这时候pushdown的时候,对于右孩子要计算出来fibab到这个点是多大才可以。一如既往的,add操作需要在maintain里加单节点特判(我也不知道为什么但是已经习惯了)还有一点注意ta一定是+的第一个,tb一定是+的第二个。

准备再用等比数列方法做一遍吧,看看是怎么个操作。。今天写了一下等比数列的操作方法,感觉比原来的方法简单emmm
继续阅读“[线段树][区间等比/等差数列修改] CodeForces – 446C DZY Loves Fibonacci Numbers”

[线段树][收敛函数][区间修改]

https://cn.vjudge.net/problem/CodeForces-920F

跟上一道HDU的题目很像,也是求快速收敛函数的和,因为有结论n的约数个数期望为sqrt(n)。每次遇到一个数就将其质因数分解然后求约数个数,这里可以开一个1e6的数组把处理过的n存下来,记忆化,就可以省下来很多时间(大概吧呵呵)。另外一个需要注意的点是收敛的终点可能是1也可能是2,我这里的方法是把读入的1全变成2,然后输出的时候减去前缀和差分就行了,因为1是怎么都不会变的,2也是不会变的,这样可以保证正确。

继续阅读“[线段树][收敛函数][区间修改]”

[线段树][优化][特征分析] HDU 4027 Can you answer these queries?

https://cn.vjudge.net/problem/HDU-4027

这个题,本身并没有用到区间修改的什么操作。。如果不加优化就基本可以认为是单点修改区间查询的线段树,但是int64的最大值也只需要6次开根号就会收敛到1,因此我们可以加一个优化就是当子区间每一个都是1的话就不再更新区间(就这一个优化真的可以搞定吗。。。)其他的都跟普通的线段树一样了,只不过初始化建树的时候需要一个单独的点修改函数来操作。
继续阅读“[线段树][优化][特征分析] HDU 4027 Can you answer these queries?”