=== ===
这里放传送门
=== ===
题解
可以发现如果一段楼房能被看见,那么它们跟原点的连线的斜率都是单调递增的。于是这就变成了一个维护上升序列的题。这里的上升序列不是最长上升子序列那样的东西,而是相当于贪心地选择,选中的子序列中的每一个元素它前面都不能存在大于等于它的元素。比如说,有一个斜率序列是1,2,4,3,4,如果是做最长上升子序列那种东西的话我们会选择[1,2,3,4]这个子序列,但这道题里面不行,因为第四个位置的3被第三个位置的4给挡住了,就看不见了,所以只能选到[1,2,4]这个子序列。
那么问题就变成了动态修改某个点的值,求整个序列里最长的这样的上升序列长度,可以用线段树来维护。首先因为这个东西和区间最大值有关,如果某个区间的最大值比较大的话它可能会挡住后面所有的区间,后面的区间就不会有贡献了。所以维护一个Max表示区间最大值。然后再维护一个val表示当前区间里最长的上升序列的长度。注意这里是仅仅考虑当前区间不考虑左右两边是什么情况,这样才好用线段树递归更新求解。
修改最大值好办,关键是如何维护val。每次输出的答案其实就是val[1],那么就考虑如何在修改以后把val给正确地update上去。在线段树中,一个区间被mid分成了[l..mid]和[mid+1..r]两个部分,显然这个区间的val至少是它左子树的那一部分,要看见右子树上的那些房子就肯定也能看见左子树最长的那一部分。那么就是如何计算右子树那一部分了。不能直接累加val[(i<<1)+1],因为前面提到过线段树的每个节点里面维护的长度信息都是不考虑左右两边的情况的,但是在update的时候,右子树必须要考虑左子树的情况。有可能左子树的Max比较大然后右子树的上升序列就被截掉了一部分,不能全部被看见。所以要继续调用过程在线段树里面查询。
调用一个类似二分的divide过程,带入一个比较参数v表示这段区间里要查找的,最小值必须大于v的一段上升序列的长度。在update的时候要对右子树节点调用这个过程,带入的比较参数就是左子树的最大值。显然右子树能看见的那一段上升序列的开头必须至少要大于左子树最大值对吧。然后就进去查询。注意这个divide过程的用途就是查询在当前