线段树还真是纠结了我好几天。由于几乎0基础,所以先研究别人AC的代码。
链接如下:http://hi.baidu.com/forverlin1204/blog/item/5b0b533110d9d595a8018e5b.html
开始不懂:为什么一个区间的高度不是0,就直接不去判断它的子区间中是否有满足题意的连续块了。
后来才发现原来是题意理解错了,一个push对应一个release。所以如果父区间的height不为0的话,子区间的height就不可能为0。
最后自己跌跌撞撞得写好了代码, 发现提交WA。对比那AC的代码发现,自己的代码就差:对于离散化后的点,如果两点间的间距大于1的
话,那么要在这两个点之间加入这两个点的终点。
表示无法理解为什么这么做,感觉离散化后的点就能正确地表示每个区间了的。所以为什么要加个点呢?后来自己在纸上模拟了下两种不同表示时候的插入情况。才发现如果不加这个点的话, 那么离散化后,原先不连续的两点就被当成连续的了。
比如输入的坐标包含1, 2, 4, 5。
转化后为0(1), 1(2), 2(4), 3(5)。于是乎,原先的2,4点变成1,2点后被当做连续的了。所以我们只需这两个点之间随便地插入一个点。
下面说一下解题的细节:
一、首先树的构造:
struct Node{
int height, right, left, cnt, r, l;
};
height: 区间的高度。push则加1,release则减一。(从图像上看好像有点有违常理)
right, left:记录区间的最右边(左边)是不是属于某个连续块的。
cnt: 记录这个区间里有多少个高度为0的连续块。
r, l: 区间的左右界限。
二、接着是如何标记
我们只需标记找到的能覆盖的最大区间就行。
由于一个区间的height为0的话肯定没有符合题意的连续块,所以push或release一个区间后,如果heigh不为0,则将它的cnt以及right和
left都赋值为0。
三、如何更新(传递标记)
做法总是递归更新,问题就在于如何根据子区间的状态来更新父区间。
首先,如果父区间的高度不为0的话,那么是不用考虑子区间的具体情况的。cnt总为0。
如果父区间高度为0,那么就要考虑子区间了。cnt等于两个子区间的cnt相加。还要注意减去重复计算的(如果有的话)。
还有就是要把子区间的right,left标记传上来。 最后,更新时还要留意区间是不是叶子节点的情况。
ps.这回终于体会到什么是真正的离散化了:只记录需要的。 之前做的那个求线段并的,只是离散化初体验,感觉不到明显的离散—_—