[IOI2015]分组

如果把一个限制\((a,b)\)视为平面上的一个点。
小组的人数是\((x,x)\)
题目的限制要求\(a<=x<=b\)就是\(a<=x\)\(x<=b\)
则平面上在\((a,b)\)左上方的无穷大的平面的小组代表的点都是当前点\((x,x)\)能取的。要取\(x\)个。
考虑一个贪心。把所有小组按照\(x\)从小到大排序。
则显然我们尽量取纵坐标最小的点最优。
这是因为纵坐标更大的点更容易能被后面的\(x\)取。坐标越小的点更容易失去覆盖机会。
这样子我们可以解决前面的部分分。
要解决此题,需要发现更好的性质。
引理1:在任何时刻,横坐标为\(i\)的点中,被覆盖的点的纵坐标一定是个连续的区间。
引理2:设\(h_i\)为横坐标为\(i\)的点中,被覆盖的点的最大高度。则\(h_i<=h_{i+1}\)
可以使用反证法。设\(j\)为第一个点满足\(h_j>h_{j+1}\)。则如果把\(j\)的一些点给\(j+1\),依然符合要求。
反复运用此操作,最后一定能调整到\(h_i<=h_{i+1}\)
把询问的所有值从小到大排序。
显然使用一个单调栈维护\(h\)。把连续相同的\(h\)缩成一个元素。
h可以表示一个折线。
在插入一个左下角为\((x,x)\)的矩形后,删除栈顶所有纵坐标\(<x\)的点。
现在栈顶的点的纵坐标\(>=x\),所以\((x,x)\)可以取栈维护的折线上的所有点。
\(r_i\)表示栈中第i个元素在栈中所对应的直线的右端点的左上,在折线上方的元素的个数。
我们先统计横坐标在\([st_{tp},a_i]\),纵坐标在\([a_i,inf]\)的点的个数。显然可以可持久化线段树。
如果点数\(<a_i\)则返回0。
否则扫描栈中的所有元素。
我们栈中右部的点数为\(p\),设剩余我们需要匹配的点数为\(pp\),则\(pp-=p\)
\(v=r_{po}-pp\)表示上面需要二分的点数。
把线段树上矩形\([st[tp],i]\)的点拿出来把纵坐标从大到小排序,然后求出删除上面\(v\)个点后剩下的点的最大横坐标\(pv\)
如果pv>栈顶元素,则删除栈顶元素,更新pp。
显然可以线段树二分解决。
综上,我们在\(n\log_2n\)时空复杂度内解决了此题。
维护栈的过程十分玄学。

posted @ 2020-09-25 17:39  celerity1  阅读(165)  评论(0编辑  收藏  举报