动态树 $\text{LCT}$ 习题篇
作者:互联网
目录
\(\text{LCT}\) 练习
P3203 [HNOI2010]弹飞绵羊
思路:
我们假设弹飞就是弹到装置 \(\text{n+1}\)
那么显然这道题就转变为:
对于每个点 \(i\) 都有一个值 \(k_i\),若是 \(i+k_i>n\) 则 \(i\to n+1\) 连一条边,否则 \(i\to i+k_i\) 连一条边
每次会修改一个点的 \(k_i\) 或询问 \(i\to n+1\) 会经过多少个点 (不包括 \(n+1\))
显然我们需要支持动态加边,动态删边,以及查询一段路径上的点的个数
很容易想到可以用 \(\text{LCT}\) 来维护
我们通过 \(\text{link(x,y)}\) 和 \(\text{cut(x,y)}\) 来完成修改操作
通过 \(\text{split(x,n+1)}\) + 维护 \(\text{splay}\) 中每个节点的子树大小来完成查询操作
最后输出的就是经过 \(\text{split}\) 操作后的 \(\text{size[n+1]-1}\)
这样就能能够解决此题
话说貌似好像可以用分块硬搞 \(\text{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] 洞穴勘测
思路:
我们需要维护三个操作:连边,删边,判断两点是否连通
显然可以用 \(\text{LCT}\) 维护
我们只需要 \(\text{link(x,y)}\),\(\text{cut(x,y)}\),以及判断 \(\text{findroot(x)}\) 是否等于 \(\text{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);
}
}
标签:LCT,ch,int,text,read,while,习题 来源: https://www.cnblogs.com/into-qwq/p/16514835.html