主席树套路小结

主席树小结

前言

特别基础的就不说了,自己\(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,m\leq 10^8\) ; \(d\leq 4*10^4\) ; \(Q\leq 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\)带入对应的主席树中算出前缀和,然后作差即可。
实现代码:戳我

posted @ 2018-04-03 19:46  GuessYCB  阅读(576)  评论(0编辑  收藏  举报