[学习笔记]线段树分治

https://www.luogu.org/blog/Miracevin/shuo-ju-jie-gou

一种离线处理方法

可以处理“具体哪个修改对询问有影响”、可以贡献不独立、可以支持插入删除

关键是把一个修改看成一个区间,每个询问是一个叶子,修改在线段树上打标记

例题6:

例题 7
• 给出一张图
• 对每个点求出强制这个点点度为 1 的最小生成树的权值

• 𝑛 ≤ 100000, 𝑚 ≤ 300000

等价于:每个边存在三段:[1,x-1],[x+1,y-1][y+1,n]

LCT维护最小生成树

i的答案再加上和i相邻的边权最小值

 

例题 8
• 给一棵树,边有边权
• 每次操作是删除一条边并加入一条边,保证操作完还是树
• 你需要维护有多少点对 𝑥, 𝑦 的路径上所有数的最大公约数是 1
• 𝑛 ≤ 100000, 𝑎𝑖 ≤ 100000, 𝑚 ≤ 30000

gcd这个东西太难搞。

反演一下得到:

ans=∑miu(d)f(d)

f(d)表示路径上的点都是d的倍数的点对的个数

也就是,所有是d的倍数的点构成的若干个联通块,f(d)=∑szi*(szi-1)/2

 

考虑对每个边出现的区间进行线段树分治

然后dfs,用按秩合并并查集维护每个d的f(d),也就是维护好联通块∑szi*(szi-1)/2

每加入一个边,枚举这个边两边的点的gcd的约数d,再对每个并查集进行维护。

栈序撤销

总共每个边只会出现sqrt(w)次,w是边权,也即gcd(x,y)

(当然LCT也可以,常数爆炸就是了)

 

例题 9
• (CTSC2016时空旅行)
• 你需要维护若干个版本的集合,每个集合元素是 𝑥, 𝑣
• 每次可以扩展一个版本,扩展内容为加一个新元素或删除一个已
有元素
• 每次询问一个 𝑦,要你在给定版本 𝑖 的集合中找出 𝑥, 𝑣 使得
𝑥 − 𝑦 2 + 𝑣 最小
• 𝑛, 𝑚 ≤ 1000000

[CTSC2016]时空旅行

也比较神仙

先推性质咯

 

发现版本扩展这个东西是一个树形结构

每个元素存在的区间,直接按照版本分的话,可以分层O(m),直接挂掉

于是考虑用dfn序!,x一定出现在x的子树里,并且删掉x这个元素操作,可以把这个区间再分成若干小区间(扣除小子树)

但是总区间数是m

为线段树分治打下基础

 

最优化的式子明显是斜率优化,维护下凸壳,横坐标是x,纵坐标是x*x+v,斜率是2y

考虑怎么处理询问:

1.dfs?不支持单点增量,只能平衡树暴力维护凸包。。。。复杂度不敢想象。

2.考虑每个修改区间对叶子询问的影响。(毕竟可以直接取min)

既然已经离线,我们可以排序!把修改按照x排序,询问按照斜率k排序,按照顺序加入线段树,

这样,每个线段树的点维护一个vector(其实是单调队列),就是这个区间的凸包,x递增,所以单点增量即可

再维护一个vector,每个询问插入的时候,k从小到大,再加入这个vector里面。然后单调队列即可。

3.考虑对每个询问,找父链上的凸包来更新答案

只用凸包的一个vector即可。

询问k从小到大,每次找整个链上的凸包做一下。每个区间可以单调队列处理。

(这题横坐标有相同的,要注意)

 

 


upda:2019.5.17

线段树分治套虚树?

询问区间放上去,关键点放上去,建立虚树再DP

O(nlog^2)

【2018 12月集训 Day2】小奇的危机

 

posted @ 2019-02-07 18:38  *Miracle*  阅读(3039)  评论(0编辑  收藏  举报