动态树 LCT 习题篇

LCT 练习#

P3203 [HNOI2010]弹飞绵羊#

思路

我们假设弹飞就是弹到装置 n+1

那么显然这道题就转变为:

对于每个点 i 都有一个值 ki,若是 i+ki>nin+1 连一条边,否则 ii+ki 连一条边

每次会修改一个点的 ki 或询问 in+1 会经过多少个点 (不包括 n+1)

显然我们需要支持动态加边,动态删边,以及查询一段路径上的点的个数

很容易想到可以用 LCT 来维护

我们通过 link(x,y)cut(x,y) 来完成修改操作

通过 split(x,n+1) + 维护 splay 中每个节点的子树大小来完成查询操作

最后输出的就是经过 split 操作后的 size[n+1]-1

这样就能能够解决此题

话说貌似好像可以用分块硬搞 qwq

#include<bits/stdc++.h>
using namespace std;

const int N=2e5+5;

inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}

int n,m;
int st[N],a[N];
int f[N],ch[N][2],sz[N],lz[N];

namespace LCT{
	#define l(x) ch[x][0]
	#define r(x) ch[x][1]
	inline void push_up(int x){
		sz[x]=1;
		if(l(x)) sz[x]+=sz[l(x)];
		if(r(x)) sz[x]+=sz[r(x)];	
	}
	inline bool rootful(int x){return l(f[x])!=x&&r(f[x])!=x;}
	inline bool chk(int x){return x==r(f[x]);}
	inline void rev(int x){swap(l(x),r(x)),lz[x]^=1;}
	inline void push_down(int x){
		if(!lz[x]) return;
		if(l(x)) rev(l(x));
		if(r(x)) rev(r(x));
		lz[x]=0;
	}
	inline void rotate(int x){
		int y=f[x],z=f[y],k=chk(x),w=ch[x][k^1];
		if(!rootful(y)) ch[z][chk(y)]=x;f[x]=z;
		if(w) f[w]=y;ch[y][k]=w;
		ch[x][k^1]=y,f[y]=x;
		push_up(y),push_up(x);
	}
	inline void splay(int x){
		int y=x,top=0,z;
		for(st[++top]=y;!rootful(y);st[++top]=y=f[y]);
		for(;top;push_down(st[top--]));
		while(!rootful(x)){
			y=f[x],z=f[y];
			if(!rootful(y))
				rotate(chk(x)==chk(y)?y:x);
			rotate(x);
		}
	}
	inline void access(int x){
		for(int y=0;x;x=f[y=x]){
			splay(x);
			r(x)=y;
			push_up(x);
		}
	}
	inline void makeroot(int x){
		access(x);
		splay(x);
		rev(x);
	}
	inline int findroot(int x){
		access(x);splay(x);
		for(;l(x);x=l(x)) push_down(x);
		splay(x);
		return x;
	}
	inline void link(int x,int y){
		makeroot(x);
		if(findroot(y)!=x) f[x]=y;
	}
	inline void cut(int x,int y){
		makeroot(x);
		if(findroot(y)==x&&f[y]==x&&!l(y)){
			f[y]=r(x)=0;
			push_up(x);
		}
	}
	inline void split(int x,int y){
		makeroot(x);
		access(y);
		splay(y);
	}
	inline int query(int x,int y){
		split(x,y);
		return sz[y];
	}
}

signed main(){
	n=read();
	for(int i=1;i<=n;++i){
		a[i]=read();
		LCT::link(i,i+a[i]>n?n+1:i+a[i]);
	}
	m=read();
	while(m--){
		int op=read(),x=read()+1,y;
		if(op==1) printf("%d\n",LCT::query(x,n+1)-1);
		else{
			y=read();
			LCT::cut(x,x+a[x]>n?n+1:x+a[x]);
			a[x]=y;
			LCT::link(x,x+a[x]>n?n+1:x+a[x]);
		}
	}
}

P2147 [SDOI2008] 洞穴勘测#

思路

我们需要维护三个操作:连边,删边,判断两点是否连通

显然可以用 LCT 维护

我们只需要 link(x,y)cut(x,y),以及判断 findroot(x) 是否等于 findroot(y) 就可以解决此题

#include<bits/stdc++.h>
using namespace std;

const int N=5e5+5;

inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}

int n,m,top;
int ch[N][2],f[N],lz[N],st[N];

namespace LCT{
	#define l(x) ch[x][0]
	#define r(x) ch[x][1]
	inline bool rootful(int x){return r(f[x])!=x&&l(f[x])!=x;}
	inline int chk(int x){return x==r(f[x]);}
	inline void rev(int x){swap(l(x),r(x));lz[x]^=1;}
	inline void push_down(int x){
		if(!lz[x]) return;
		if(l(x)) rev(l(x));
		if(r(x)) rev(r(x));
		lz[x]=0;
	}
	inline void rotate(int x){
		int y=f[x],z=f[y],k=chk(x),w=ch[x][k^1];
		if(!rootful(y)) ch[z][chk(y)]=x;f[x]=z;
		if(w) f[w]=y;ch[y][k]=w;
		ch[x][k^1]=y,f[y]=x;
	}
	inline void splay(int x){
		int y=x,top=0,z;
		for(st[++top]=y;!rootful(y);st[++top]=y=f[y]);
		for(;top;push_down(st[top--]));
		while(!rootful(x)){
			y=f[x],z=f[y];
			if(!rootful(y))
				rotate(chk(x)==chk(y)?y:x);
			rotate(x);
		}
	}
	inline void access(int x){
		for(int y=0;x;x=f[y=x]){
			splay(x);
			r(x)=y;
		}
	}
	inline void makeroot(int x){
		access(x);
		splay(x);
		rev(x);
	}
	inline int findroot(int x){
		access(x);splay(x);
		for(;l(x);x=l(x)) push_down(x);
		splay(x);
		return x;
	}
	inline void link(int x,int y){
		makeroot(x);
		if(findroot(y)!=x) f[x]=y;
	}
	inline void cut(int x,int y){
		makeroot(x);
		if(findroot(y)==x&&f[y]==x&&!l(y))
			f[y]=r(x)=0;
	}
	inline int query(int x,int y){
		return findroot(x)==findroot(y);
	}
}

signed main(){
	n=read(),m=read();
	while(m--){
		char s[20];
		scanf("%s",s);
		int x=read(),y=read();
		if(s[0]=='Q') puts(LCT::query(x,y)?"Yes":"No");
		if(s[0]=='C') LCT::link(x,y);
		if(s[0]=='D') LCT::cut(x,y);
	}
}

作者:Into_qwq

出处:https://www.cnblogs.com/into-qwq/p/16514835.html

版权:本作品采用「qwq」许可协议进行许可。

posted @   Into_qwq  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示