zoukankan      html  css  js  c++  java
  • 主席树套路小结

    主席树小结

    前言

    特别基础的就不说了,自己(Baidu)
    直接上套路好了。
    (P.s)(YL)的主席树总结这里出门左转:戳我

    一.树上路径第K大

    考虑固定一个点为根,然后从上往下建值域主席树。
    每个点(u)的主席树从它的父亲(fa_u)转移过来。
    查询:
    类似求(LCA)的方法,但注意要保留(LCA)这个点:

    [E_{<u,v>} = Tree_u + Tree_v - Tree_{lca} - Tree_{fa(lca)} ]

    直接四个主席树加加减减即可。
    实例题目:BZOJ2588 Count on a treeSDOI2013 森林

    二.子段、子区间第K小

    把找第(K)(大/小)子区间转化为找第(K)(小/大)前缀。
    不解释直接给题目。
    实例题目:NOI2010 超级钢琴

    三.局部修改(无后效性)操作

    注意到主席树维护的是前缀。
    所以有些修改利用这个性质可以暴力修改。

    例题:G. Can Bash Save the Day?
    题目大意:
    给定一棵(n)个节点的树和一个排列A,有两种操作:
    %%% $1 x (:交换)A_x , A_{x+1}$。
    %%% $2 l r v (:求)sum_{i=l}^r dist(A_i , v)$。
    强制在线。

    首先求第二问的那个套路大家都会吧,不会见HNOI2015 开店这道题。
    注意到主席树是维护的前缀,
    交换(A_x)(A_{x+1})其实只有第(x)棵主席树(Tree_x)变化了。
    重新(Copy)一下(root_{x-1}),然后暴力重建即可。 代码戳我

    四.有关区间不重复个数的问题

    关于区间不重复数的个数可以用主席树做。
    不要维护值域,而应该要维护位置。

    例题:
    一共(Q)次询问,每次询问([l,r])中不重复的数的个数。

    从前往后一个一个数加。
    如果这个数在之前出现了,并且出现位置为(p),那么在那个位置-1。
    然后在当前位置+1即可。
    很好理解,出现在后面肯定是更优秀的!
    查询([l,r])时,就是查询(Tree_r)([l,r])区间内的和(只查一棵主席树!)。
    实例题目:SP3267 DQUERY D-queryCF813E Army Creation
    其中第二题的代码见这里:戳我

    五.二分后的0/1转换套路

    0/1的套路同HEOI2016 排序
    二分一个答案,然后把大于它的设为1,小于它的设为0或-1。
    然后各种线段树搞事情(如最大子段和等)来进行(check)
    主席树的作用主要是优化转化0/1这个过程。
    把所有数按照大小(sort)一遍(记录原位置)。
    然后 从小到大/从大到小 依次加入数字,每次把对应位置的数变为-1/0/1。
    具体的 初始化 和 加入顺序 依题目而定,需要仔细分析。
    这个强烈推荐去看一下这两个题目:
    实例题目:CF484E Sign on Fence , 国家集训队 middle
    这两题的代码看这里:戳我 ;

    六.主席树维护一次函数

    例题
    (n*m)的矩阵,初始权值都为(0)
    首先会有(d)个修改操作形如:
    ([ (x_1,y_1,x_2,y_2))(V ]),把这个范围内的方格的权值+(V)
    在修改结束后,会有(Q)个询问,
    每次询问一个矩阵((x_1,y_1,x_2,y_2))内的权值和。
    数据范围:(n,mleq 10^8) ; (dleq 4*10^4) ; (Qleq 10^5) ; 答案对(2^{64})取模。

    首先取模(2^{64})直接开(unsigned long long)自然溢出即可。
    主席树维护一次函数.......
    首先对每一个(y)坐标建立一棵主席树,每一棵主席树下标维护(x)坐标。
    一次函数是什么意思呢?
    显然,每一列((x)坐标)的权值前缀和是一个关于(y)坐标的分段一次函数
    一次函数具有可加性。
    所以假设我们可以维护这个一次函数,
    那么每次查询的答案就是(sum_{x=x_1}^{x_2} [ f(y2)_x - f(y_1-1)_x ])
    (y)坐标离散化,(x)坐标直接动态开点。
    考虑修改,设修改的(y)坐标为([l,r]),所加权值为(V)
    对于斜率的拔高,我们可以列出方程:
    (Delta k*l + Delta b = V)
    (Delta k*r + Delta b = V(r-l+1))
    就是对应这两个点增长了多少,解得:(Delta k = V , Delta b = V(1-l))
    所以对应的在第(l)棵主席树上加上(Delta k)(Delta b)
    (r+1)的位置上斜率恢复正常,截距增高,对应到 这个点的前缀和增长量 有:
    ((Delta k + k')(r+1) + (Delta b + b') = (r-l+1)V)
    ((Delta k + k') = 0)
    解得:(k' = -Delta k , b' = -rV)
    在第(r+1)棵线段树上加上(k')(b'),使得斜率恢复正常。
    查询的时候直接把(y_1,y_2)带入对应的主席树中算出前缀和,然后作差即可。
    实现代码:戳我

  • 相关阅读:
    Part 11 Search filter in AngularJS
    Part 10 AngularJS sort rows by table header
    Part 9 Sorting data in AngularJS
    Part 8 AngularJS filters
    Part 7Handling events in AngularJS
    Part 6 AngularJS ng repeat directive
    PHP单一入口应用程序概述
    SVN
    跨平台的.NET集成开发环境:MonoDevelop
    PHP中使用KindEditor
  • 原文地址:https://www.cnblogs.com/GuessYCB/p/8710754.html
Copyright © 2011-2022 走看看