LCT

原理

懒得讲了

应用(你会发现这就是(fAKe_Hu)的题单)

维护链信息

luoguP1501

维护链懒标记的裸题

弹飞绵羊

每个点向右边弹到的点连边,超过(n)的所有点连向(n+1),最后形成一棵树,一个点(splay)到根后左儿子的数量(深度)就是最后答案

luoguP4332

记录一个节点的权值为(0-3)表示接受的(1)状态的数量

考虑每次更改一次节点状态,会自底部向上修改连续一段节点的状态

我们可以平衡树上二分找第一个权值为(1/2)的节点,也可以直接数组记录,然后把它换到根

注意修改完之后记录(1/2)的数组要交换,如果这个节点不存在就把整条链都修改

动态维护连通性

luoguP2147

裸题,(link)(cut)操作

luoguP2542

常见套路是删边变倒序加边

先将没有被删去的边连起来,我们把每条边中间加一个点,然后把新加的点且在原图中是割点的的点权都变成(1)

倒序操作序列,加边的时候如果两个点不连通就直接连边,让新加的点边权为(1)

否则把那个点到根的权值全部变为(0)

查询操作就是两点间的权值

bzoj2959

(1)操作:连接两个跑道,如果已经联通,那么就把路径上所有点用并查集合并,把所有点的权值给根节点

(2)操作:修改该节点根节点权值

(3)操作:求链和

没写,口胡的(

动态维护生成树

水管局长

删边变逆序加边

(link)一条边时,如果两点已经联通,那么二分找到边权最大的边(边化点),然后替换掉

最小差值生成树

将边权从小到大排序插入(LCT),形成生成树之后每次把边权最小的边换掉,计算答案

膜法森林

将边按一个关键字排序,维护另一个关键字的最小生成树

维护子树

大融合

求一条边两个端点虚子树点数之和

其实就两步,(link)的时候一个节点加上另一个节点总儿子数,(access)的时候加上原右子树减去新右子树

QTREE5

这是个神仙状态

(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))

LOJ558

包括(link)(cut)操作的维护子树

注意点就是(reverse)的时候要把(dp1)(dp2)一起交换,同时(cut)的时候必须把连个点都喝边代表的虚点断开,否则边权会随机加到一棵(splay)

维护染色联通块

luoguP2173

每种颜色开一个(LCT),大力维护就行了

luoguP3703

我们仍然考虑每个(splay)维护同一种颜色

(1)操作:其实就是一个(access)操作

(2)操作:考虑一个节点到根节点的权值,就是这个节点到根节点的虚边数量,但是每次(access)更新时会更新一整个子树,所以我们考虑(dfs)序上建线段树维护,询问时考虑树

上差分,由于每个颜色都是到根节点的一条链,所以可以直接差分

(3)操作:(dfs)序上区间最值问题

QTREE6

点化边模型,显然暴力操作会被菊花图卡到自闭,我们考虑把每个点的颜色给这个点在树上的父亲边

(1)号节点没有父边,需要加一个虚拟节点

变化颜色时将该店与父亲节点的边更换(LCT),这样发现每个连通块中除了根节点都是同一颜色

查询时输出根节点的右儿子的答案

奇怪操作

睡觉困难综合症

四个状态,(0)从根走到当前,(1)从根走到当前,(0)从当前到根,(1)从当前到根

合并的式子是

t0=(~x.t0&y.t0)|(x.t0&y.t1);
t1=(~x.t1&y.t0)|(x.t1&y.t1);

对于(0)起始,就是前一段是(0)的位走后一段(0)起始的答案或上前一段是(1)的位走后一段(1)起始的答案,(1)起始同理

没了

UOJ207

考虑到对于一段路径((x,y)),如果所有路径都经过((x,y)),那么以(x)为根,必然都有且只有一个端点在(y)子树内

每条路径(rand)一个权值出来,将路径的两个端点异或上这个权值,(split(x,y))之后判断(x)子树异或和是否等于所有边权异或和

luoguP3348

逐个操作分析。。

(1)操作,显然对于所有树都长一个节点是无所谓的

(2)操作就讲究了,会发现(l)(l-1)颗树的区别只是一大堆节点长哪上面了,转移一大堆节点我们可以套路的弄个虚点出来,把虚点连生长节点下面,生长的时候长虚点下面,更

改生长节点的时候把虚点换个位置就行了

查询:规定了根节点的情况下并不能换根,考虑树上差分,对于虚点点权应该为(0),这里有个小技巧:(access)(lca)

(access(x)),再(access(y)),最后一次操作的(y)节点就是(lca)

luoguP4338

考虑每个点的贡献,对于一个点来说,最优的操作方式肯定是一个每个子树内的点交替(access),因为连续两个同一子树内的点(access)不对答案作出贡献

然而有可能某个子树点数过大导致无法实现。

具体来说,一个点对答案的贡献是(min{S_x-1,2*(S_x-max{S_{son_x}})})

(max{S_{son_x}}>frac{S_x+1}{2})(min)取后面

但是我们还有修改操作

对于一个点(a_i+w),能影响的只有这个点到根的路径,如果路径中本身就有些点子树过大,那么是不用修改的

哪些点用修改呢?就是将整棵树重链剖分后的轻边的父节点,这些点不超过(logn)

证明完复杂度我们可以用(lct)(access)来完成对路径上虚边的判断

一些细节可以参照(fAKe_Hu)(code)。。。

在美妙的数学王国中畅游

乱七八糟的东西不好维护,考虑每个点维护一个多项式

所以我们把(e^{ax+b})(sin(ax+b))泰勒展开一下子变成多项式维护起来

然后就基本裸(lct)

原文地址:https://www.cnblogs.com/knife-rose/p/12375608.html