有根树

显然我们会选择\(w\)最大的点加入答案。
当被插入的\(w\)值越小,插入的节点数会越大。
被插入的\(w\)值随着插入的节点数变得越来越小。
所以我们其实是要求两个单调函数的交点。
可以把\(w\)从大到小排序后把最大的值插入到解中。
由于\(w\)的权值只有\(n\)种,所以可以桶排序。
考虑加速这个过程。
一个想法是二分,但是是不可行的。
如果要二分,则我们需要求第k大,除了分块别无它法。
注意在二分做法中到我们并没有利用以前的解的信息,所以可以进行调整。
假设现在我们选择的点数是\(X\),没被选的点\(w\)最大是\(Y\),被选的点的最小\(w\)\(z\)
根据条件,一定满足\(Y<z\)
如果我们正确的维护的\(w\)值,调整的过程就是把\(z\)从C中删除,或者把\(Y\)插入C。
这样子必定会是X减少/Y增加。
如果插入一个点,最多会有一个点的\(w\)\(>Y\)。如果有多个,则这些点肯定是父亲/儿子关系,一个的值肯定比另一个大。
这可能会导致\(Y>z\)
我们要正确的维护\(w\),当\(Y>z\),要把\(Y\)\(z\)交换。
交换后调整即可。
由于最多有一个点使得权值\(=Y+1\),所以我们最多把1个节点从非C集合放在C集合中。
删除同理。
注意到我们维护的是不在C中的最大值,在C中的最小值。
问题等同于链+,全局最大值,使用轻重链剖分+线段树维护。
我们得到了一个时间复杂度\(O(n\log_2^2n)\),空间复杂度\(O(n)\)的算法。
这样子时间复杂度比正解高,但是如果常数较小可以通过。
当我们调整的时候,其实我们在做这么一个过程:
\(X<Y\),如果有节点的权值\(=X\),且该节点不在\(C\)中,则\(X+=1\),否则\(Y-=1\)
\(X>Y\),如果有节点的权值\(=Y\),且该节点在\(C\)中,则\(X-=1\),否则\(Y+=1\)
可以看做暴力的寻找最大/小值。
根据之前的分析,最多会跳1次。
还是考虑对树进行轻重链剖分。对于每条重链,根据之前的分析,我们维护最大/最小值。
会发现,对于一条重链,只有最顶端/底端的节点会成为最大/小值。
所以可以对每条重链使用一个set维护最小/大值。
除此之外,我们还需维护最大/小值中,是否有权值为X的点。
在我们插入/删除一个点后,我们可能需要对它能够跳到的重链上的点的权值+1/-1,而这个过程有nlogn次。
可以使用链表。在不在C/在C元素的桶中,开n个链表,第x个链表维护权值x的点。
在对节点+1/-1时,从对应的链表中删除原来值的节点,插入新的值的节点。
借助dfs序,我们可以在\(O(1)\)的时间复杂度内求出一个点的权值是否要被修改,这样子可以\(O(1)\)维护权值。
在插入/删除点的时候,由于我们通过以前的信息不能知道这个点的权值,使用dfs序+树状数组求出它的权值。同时更新当前链上的最大/小值。
这个操作在一个插入/删除操作内最多出现1次,时间复杂度为\(\log_2n\)
综上,我们使用\(O(n)\)的空间,\(O(n\log_2n)\)的时间解决了这个问题。
总结:这道题的核心想法是转化模型,优化调整的过程。
标准算法充分的利用了此题的性质。
这是我认为在NOI WC2020 最好的一道题。

posted @ 2020-09-15 09:14  celerity1  阅读(168)  评论(0编辑  收藏  举报