[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。
显然可以线段树二分解决。
综上,我们在(nlog_2n)时空复杂度内解决了此题。
维护栈的过程十分玄学。

原文地址:https://www.cnblogs.com/ctmlpfs/p/13731502.html