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
也比较神仙
先推性质咯
发现版本扩展这个东西是一个树形结构
每个元素存在的区间,直接按照版本分的话,可以分层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)