WC2020有根树

题意

给定一棵包含 \(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)\) 的最小值。

\(n\le 5\times 10^5,q\le 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:存在最优解满足:\(\min\limits_{x\in A}w_x\ge \max\limits_{x\in B}w_x\)条件1)。

证明:
考虑目前\(\min\limits_{x\in A}w_x< \max\limits_{x\in B}w_x\)
我们将\(A\)\(w\)最小的点,移到\(B\)内,显然答案不会变劣


通过简单分析结论1,为方便下文叙述,我们将\(\{i|i\in S\}\)看成一个\(w\)降序排列的序列
定义2:对于任意集合\(T\),称\(T\)\(w\)最大的为\(T\)\(w\)最小的为\(T\)尾。


结论2:存在唯一的最优解在满足条件1的情况下满足:\(\min\limits_{x\in A}w_x\ge|A|\ge \max\limits_{x\in B}w_x\)

证明:
\(\text{(i)}\)我们先证明存在最优解满足该条件
在满足条件1的情况下
\(|A|>\min\limits_{x\in A}w_x\),将\(A\)尾移到\(B\)
\(\max\limits_{x\in B}w_x>|A|\),将\(B\)首移到\(A\)
唯一需要考虑的情况在,是否通过移动一个元素,\(|A|\)\(|A|>\min\limits_{x\in A}w_x\)\(\max\limits_{x\in B}w_x>|A|\)横跳
\(A\)尾移到\(B\)首,\(|A|-1\ge \min\limits_{x\in A}w_x=\max\limits_{x\in B'}w_x\)(令\(B'\)为移动后的\(B\),下文同理)
\(B\)首移到\(A\)尾同理
\(\text{(ii)}\)再证明存在唯一
顺着上面的思路
若满足\(\min\limits_{x\in A}w_x\ge|A|\ge \max\limits_{x\in B}w_x\)
\(A\)尾移到\(B\)
\(|A|-1<\min\limits_{x\in A}w_x=\max\limits_{x\in B'}w_x\)
\(B\)首移到\(A\)尾同理


我们构造一个不考虑时间复杂度的算法(根据简单分析,这部分与树基本无关)
根据结论2的证明,很简单能构造出\(q=0\)的思路
考虑加点/删点

  • 加点,将点随意加到\(A\)\(B\)
  • 删掉,将点从所在集合删除

然后先维护\(\min\limits_{x\in A}w_x\ge \max\limits_{x\in B}w_x\),再分别维护\(\min\limits_{x\in A}w_x\ge|A|\)\(|A|\ge \max\limits_{x\in B}w_x\)
通过简单的分析,移动次数是\(O(1)\)


考虑进时间复杂度,这里需要快速维护:\(\min\limits_{x\in A}w_x\)及该元素,\(\max\limits_{x\in B}w_x\)及该元素

比较自然的想法是,重链剖分,线段树维护。
时间复杂度\(O(nlog^2n)\)。(\(n,q\)同阶)

具体的,在加入/删除元素,通过重链剖分+线段树维护所有的\(w_i\)
线段树维护\(mi,mx\)(分别表示\(\min\limits_{x\in A}\)\(\max\limits_{x\in B}\)
对于线段树叶子节点\(u\),为了代码的简洁,这里\(\notin 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\)满足\(\min\limits_{x\in A}w_x\ge|A|\ge \max\limits_{x\in B}w_x\)的。
    考虑设阈值\(S=|A|\)(注意之后\(A\)的变化不会影响这个\(S\)
    这里对于每个\(\in B\)的元素,若其值\(>S\),我们先将其加入\(A\)(这里加入的点数是\(O(1)\)),以满足\(\min\limits_{x\in A}w_x\ge S\ge \max\limits_{x\in B}w_x\)
    在调整完后,有\(|A|\ge S\)\(\min\limits_{x\in A}w_x\ge S\ge \max\limits_{x\in B}w_x\)
    可以\(O(1)\)的调整使其满足\(\min\limits_{x\in A}w_x\ge |A|\ge \max\limits_{x\in B}w_x\)
    具体实现
    我们将\(A\)尾移到\(B\)头,由于\(A\)尾的值\(\ge S\),通过链表查询值\(=S\)\(\in A\)的点
    (对于\(A,B\)均维护两个值域链表)
    若存在,则移动至\(B\)头;若不存在则\(S++\)
    显然当\(|A|=S\)时可以满足\(\min\limits_{x\in A}w_x\ge |A|\ge \max\limits_{x\in B}w_x\)
    显然这里\(|A|\)\(S\)的移动都是\(O(1)\)的。
  • 删除点\(u\)
    与加入类似,设阈值\(S=|A|\)
    对于每个\(\in A\)的元素,若其值\(<S\),将其加入\(B\)
    这里不再赘述,可以看代码理解。

至此,我们将时间复杂度优化至了\(O(nlogn)\)

posted @ 2021-01-11 20:04  Grice  阅读(302)  评论(0编辑  收藏  举报