题意
给定一棵包含 (n) 个结点的有根树,(1) 号点为根结点。
对于一个结点集合 (S),在 (S) 中的结点 (u),定义 (w_u) 的值为 (u) 的子树中(包括 (u) 本身)被包含在集合 (S) 内的结点数,对于不在 (S) 中的结点,(w_u=0)。
你选择一个包含根结点的连通块 (C)。记 (a) 表示连通块 (C) 中被包含在集合 (S) 内的结点数,(b) 表示不在连通块 (C) 中的结点的 (w) 值最大值,若不存在不在 (C) 中的结点,则 (b=0),需要最小化 (max(a,b))。
(q) 次操作,每次会令集合 (S) 加入或删除一个结点,请你对每次操作后的集合 (S) 给出 (max(a,b)) 的最小值。
(nle 5 imes 10^5,qle 10^6)
提醒
把(O(nlog^2n))及(O(nlogn))(默认(n,q)同阶)的代码放到这里了:Code
(O(nlog^2n)):A1.cpp;(O(nlogn)):A2.cpp
如果在对下面分析有疑惑,必要时可以结合代码理解
做法
定义1:令(S)为题意中加入的点集,令(A)为连通块包含的点集,令(B)为连通块不包含的点集。
简单分析
首先不要被这题的树迷惑了
我们肯定把较大的点往(A)里选,那么连通块性质显然是成立的
树的部分在于,我们动态加点删点,如何在({w_i})变化的同时维护(A,B)
结论1:存在最优解满足:(minlimits_{xin A}w_xge maxlimits_{xin B}w_x)(条件1)。
证明:
考虑目前(minlimits_{xin A}w_x< maxlimits_{xin B}w_x)
我们将(A)内(w)最小的点,移到(B)内,显然答案不会变劣
通过简单分析与结论1,为方便下文叙述,我们将({i|iin S})看成一个(w)降序排列的序列
定义2:对于任意集合(T),称(T)中(w)最大的为(T)首,(w)最小的为(T)尾。
结论2:存在唯一的最优解在满足条件1的情况下满足:(minlimits_{xin A}w_xge|A|ge maxlimits_{xin B}w_x)。
证明:
( ext{(i)})我们先证明存在最优解满足该条件
在满足条件1的情况下
若(|A|>minlimits_{xin A}w_x),将(A)尾移到(B)首
若(maxlimits_{xin B}w_x>|A|),将(B)首移到(A)尾
唯一需要考虑的情况在,是否通过移动一个元素,(|A|)在(|A|>minlimits_{xin A}w_x)与(maxlimits_{xin B}w_x>|A|)横跳
将(A)尾移到(B)首,(|A|-1ge minlimits_{xin A}w_x=maxlimits_{xin B'}w_x)(令(B')为移动后的(B),下文同理)
将(B)首移到(A)尾同理
( ext{(ii)})再证明存在唯一
顺着上面的思路
若满足(minlimits_{xin A}w_xge|A|ge maxlimits_{xin B}w_x)
将(A)尾移到(B)首
(|A|-1<minlimits_{xin A}w_x=maxlimits_{xin B'}w_x)
将(B)首移到(A)尾同理
我们构造一个不考虑时间复杂度的算法(根据简单分析,这部分与树基本无关)
根据结论2的证明,很简单能构造出(q=0)的思路
考虑加点/删点
- 加点,将点随意加到(A)或(B)
- 删掉,将点从所在集合删除
然后先维护(minlimits_{xin A}w_xge maxlimits_{xin B}w_x),再分别维护(minlimits_{xin A}w_xge|A|)和(|A|ge maxlimits_{xin B}w_x)
通过简单的分析,移动次数是(O(1))的
考虑进时间复杂度,这里需要快速维护:(minlimits_{xin A}w_x)及该元素,(maxlimits_{xin B}w_x)及该元素
比较自然的想法是,重链剖分,线段树维护。
时间复杂度(O(nlog^2n))。((n,q)同阶)
具体的,在加入/删除元素,通过重链剖分+线段树维护所有的(w_i)值
线段树维护(mi,mx)(分别表示(minlimits_{xin A})与(maxlimits_{xin B}))
对于线段树叶子节点(u),为了代码的简洁,这里(
otin S)的点的(w)值也为子树中(in S)的点数
若该点(in A),(mi=w_u),否则(mi=w_x+infty)
若该点(in B),(mx=w_u),否则(mx=w_x-infty)
若弱化此题,单纯维护(w),可以用lct做到(O(nlogn)),但常数也十分大,可能可以通过,但未免太浪费此题的性质了。
我们对每条重链维护至多两个pair(这条链中(in A)的最大(w)及对应的点;(in B)的最小(w)及对应的点)
我们不再需要知道整个点集的(w)值,而是所有链中的pair,其为有效点(可能成为最值)
那么在加入/删除点(u)时(暂时不考虑(u)的值),对(u)至根的每条重链,可以(O(1))地修改其值
若之后需要得知某点的值,可以(O(logn))的查询属于其子树的点数(通过树状数组实现)。
关键在于如何将(A,B)调整为(A',B')
- 加入点(u)
我们之前维护的(A,B)满足(minlimits_{xin A}w_xge|A|ge maxlimits_{xin B}w_x)的。
考虑设阈值(S=|A|)(注意之后(A)的变化不会影响这个(S))
这里对于每个(in B)的元素,若其值(>S),我们先将其加入(A)(这里加入的点数是(O(1))),以满足(minlimits_{xin A}w_xge Sge maxlimits_{xin B}w_x)。
在调整完后,有(|A|ge S)和(minlimits_{xin A}w_xge Sge maxlimits_{xin B}w_x)。
可以(O(1))的调整使其满足(minlimits_{xin A}w_xge |A|ge maxlimits_{xin B}w_x)。
具体实现:
我们将(A)尾移到(B)头,由于(A)尾的值(ge S),通过链表查询值(=S)且(in A)的点
(对于(A,B)均维护两个值域链表)
若存在,则移动至(B)头;若不存在则(S++)
显然当(|A|=S)时可以满足(minlimits_{xin A}w_xge |A|ge maxlimits_{xin B}w_x)。
显然这里(|A|)与(S)的移动都是(O(1))的。 - 删除点(u)
与加入类似,设阈值(S=|A|)
对于每个(in A)的元素,若其值(<S),将其加入(B)
这里不再赘述,可以看代码理解。
至此,我们将时间复杂度优化至了(O(nlogn))。