原理
懒得讲了
应用
维护链信息
维护链懒标记的裸题
每个点向右边弹到的点连边,超过\(n\)的所有点连向\(n+1\),最后形成一棵树,一个点\(splay\)到根后左儿子的数量(深度)就是最后答案
记录一个节点的权值为\(0-3\)表示接受的\(1\)状态的数量
考虑每次更改一次节点状态,会自底部向上修改连续一段节点的状态
我们可以平衡树上二分找第一个权值为\(1/2\)的节点,也可以直接数组记录,然后把它换到根
注意修改完之后记录\(1/2\)的数组要交换,如果这个节点不存在就把整条链都修改
动态维护连通性
裸题,\(link\)和\(cut\)操作
常见套路是删边变倒序加边
先将没有被删去的边连起来,我们把每条边中间加一个点,然后把新加的点且在原图中是割点的的点权都变成\(1\)
倒序操作序列,加边的时候如果两个点不连通就直接连边,让新加的点边权为\(1\)
否则把那个点到根的权值全部变为\(0\)
查询操作就是两点间的权值
\(1\)操作:连接两个跑道,如果已经联通,那么就把路径上所有点用并查集合并,把所有点的权值给根节点
\(2\)操作:修改该节点根节点权值
3#操作:求链和
没写,口胡的(
动态维护生成树
删边变逆序加边
\(link\)一条边时,如果两点已经联通,那么二分找到边权最大的边(边化点),然后替换掉
将边权从小到大排序插入\(LCT\),形成生成树之后每次把边权最小的边换掉,计算答案
[膜法森林](https://www.luogu.com.cn/problem/P2387)
将边按一个关键字排序,维护另一个关键字的最小生成树
维护子树
求一条边两个端点虚子树点数之和
其实就两步,\(link\)的时候一个节点加上另一个节点总儿子数,\(access\)的时候加上原右子树减去新右子树
这是个神仙状态
\(dp1[x],dp2[x]\)分别表示在\(splay\)中\(x\)子树里面深度最浅的点最近的白点的距离 和 深度最深的点最近的白点的距离
每个节点开个\(multiset\)维护下虚子树内白色点的最小距离
inline int fir(int x) { return (!q[x].empty())?(*q[x].begin()):inf; } inline void pushup(int x) { str[x]=str[son[x][0]]+str[son[x][1]]+1; dp1[x]=min(dp1[son[x][0]],str[son[x][0]]+min(val[x]?0:inf,min(fir(x),dp1[son[x][1]]+1))); dp2[x]=min(dp2[son[x][1]],str[son[x][1]]+min(val[x]?0:inf,min(fir(x),dp2[son[x][0]]+1))); }
注意\(access\)的时候更新\(multiset\)
inline void access(int x) { for(int y=0;x;x=f[y=x]) { splay(x); q[x].insert(dp1[son[x][1]]+1); son[x][1]=y; it=q[x].lower_bound(dp1[son[x][1]]+1); if(it!=q[x].end()&&(*it)==dp1[son[x][1]]+1) q[x].erase(it); pushup(x); } }
最后答案是将\(x\)旋转到根之后的\(dp2[x]\)
我们在这里选择不\(makeroot\),因为需要翻转子树很麻烦,修改时直接修改\(val[x]\)的状态然后\(pushup(x)\)
包括\(link\)和\(cut\)操作的维护子树
注意点就是\(reverse\)的时候要把\(dp1\)和\(dp2\)一起交换,同时\(cut\)的时候必须把连个点都喝边代表的虚点断开,否则边权会随机加到一棵\(splay\)上
\(<=to\ be\ pigeon\)
来源:https://www.cnblogs.com/knife-rose/p/12375608.html