zoukankan      html  css  js  c++  java
  • 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)) 的最小值。

    (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))

  • 相关阅读:
    电子招投标应用系统连载(一)-开标系统
    js实现一个简单钟表动画(javascript+html5 canvas)
    ,net core mvc 文件上传
    echarts显示X轴最后一个lable
    C# 解压gzip文件(.tgz)
    【转】C#计算两坐标点距离
    用file标签实现多图文件上传预览
    c#数据批量插入
    Asp.net 中ViewState,cookie,session,application,cache的比较
    ASP.NET MVC从请求到响应发生了什么
  • 原文地址:https://www.cnblogs.com/Grice/p/14263953.html
Copyright © 2011-2022 走看看