卧薪尝胆,厚积薄发。
ZJOI2017 线段树
Date: Wed Feb 27 08:49:50 CST 2019
In Category:
NoCategory
Description:
给一棵广义线段树,设
$S_{[l,r]}$
表示定位区间
$[l,r]$
定位到的点的集合,多次询问
$u,l,r$
表示求:
$$
\sum_{v\in S_{[l,r]}}dis(u,v)
$$
$1\leqslant n\leqslant 2\times 10^5$
Solution:
先解决第一个问题,给一棵广义线段树和一个询问区间
$[l,r]$
,如何求出
$S_{[l,r]}$
,首先找到在线段树上最短的完全包含询问区间的原区间,也就是第一个开始向左向右同时递归的区间,这个就是
$[l,l]$
和
$[r,r]$
在线段树上的
$LCA$
,从这个点往左右两边看,会发现是一个前缀和一个后缀,我们需要把这些区间找出来。
为了做到这一点,我们可以定义一个”广义树状数组“,具体来说就是设每个点的
$father$
为它向后找找到的第一个紧挨着他的区间,或者说是每个点只存储半边的信息,画个图说比较清楚:
这三个图分别是原树、查询后缀时候的树和查询前缀时候的树,不难发现这个和树状数组非常像,事实上树状数组的本质就是只保留左儿子的zkw线段树,但是在这棵树上由于划分不平均,因此不能用
$lowbit$
找父亲,我们必须手动把这棵“广义树状数组”建出来,具体构建方法是设
$fa[i]$
表示
$i$
这个点在广义树状数组上的
$fa$
,加入我们现在构建的是前缀树,也就是最后一个图的情况,我们可以先
$fa[lc]=fa[k]$
,然后
$dfs$
左儿子,然后
$fa[rc]=lc$
,
$dfs$
右儿子,然后删掉所有右儿子的点,就得到了一棵广义树状数组。
有了这个,我们就可以求出在线段树上给出一个区间会被分到哪些区间上,就是从每个位置开始往上跳一直跳到跳出去为止,考虑化简答案:
$$
\begin{align}
ans&=\sum_{v\in S_{[l,r]}}dis(u,v)\\
&=\sum_{v\in S_{[l,r]}}dep[u]+dep[v]-2\times dep[LCA(u,v)]\\
&=|S_{[l,r]}|\times dep[u]+\sum_{v\in S_{[l,r]}}dep[v]-2\times\sum_{v\in S_{[l,r]}}dep[LCA(u,v)]
\end{align}
$$
前两项都很好求,麻烦的是最后一项,好像直接搞很不好做,因为
$S_{[l,r]}$
不一定是一个区间,但是一定是连续的深度递增的一条链,于是可以差分成
$(1,p)-(1,fa[q])$
,然后从
$1$
开始
$dfs$
每次插入删除元素就好了。
Code:
Copyright © 2020
wjh15101051
ღゝ◡╹)ノ♡