[学习笔记]Link-Cut Tree
我终于理解了 \(LCT\)!!!想不到小蒟蒻有一天理解了!!!
1、【模板】Link Cut Tree
存个板子
#include <bits/stdc++.h>
using namespace std;
const int maxn=300000+10;
int n,m,a[maxn],ch[maxn][2],fa[maxn],val[maxn],sta[maxn],top;
bool rev[maxn];
inline void reverse(int x){
swap(ch[x][0],ch[x][1]);
rev[x]^=1;
}
inline void pushup(int x){
val[x]=val[ch[x][0]]^val[ch[x][1]]^a[x];
}
inline void pushdown(int x){
if(rev[x]){
rev[x]=0;
if(ch[x][0]) reverse(ch[x][0]);
if(ch[x][1]) reverse(ch[x][1]);
}
}
inline bool nrt(int x){
return ch[fa[x]][0]==x||ch[fa[x]][1]==x;
}
inline void rotate(int x){
int y=fa[x],z=fa[y],k=(x==ch[y][1]),u=ch[x][k^1];
if(nrt(y)) ch[z][ch[z][1]==y]=x;
ch[y][k]=u;ch[x][k^1]=y;
if(u) fa[u]=y;fa[y]=x;fa[x]=z;
val[x]=val[y];pushup(y);
}
inline void splay(int x){
int y,z;top=0;
for(y=x;nrt(y);y=fa[y]) sta[top++]=y;
pushdown(y);
while(top) pushdown(sta[--top]);
while(nrt(x)){
y=fa[x],z=fa[y];
if(nrt(y)) rotate((ch[y][1]==x)^(ch[z][1]==y)?x:y);
rotate(x);
}
}
inline void access(int x){
int y=0;
for(;x;y=x,x=fa[x])
splay(x),ch[x][1]=y,pushup(x);
}
inline void makeroot(int x){
access(x),splay(x),reverse(x);
}
inline int findroot(int x){
access(x),splay(x);
for(;ch[x][0];x=ch[x][0]) pushdown(x);
return x;
}
inline void split(int x,int y){
makeroot(x),access(y),splay(y);
}
inline void link(int x,int y){
makeroot(x);
if(findroot(y)!=x)
fa[x]=y,pushup(y);
}
inline void cut(int x,int y){
makeroot(x);
if(findroot(y)==x&&fa[x]==y&&!ch[x][1])
fa[x]=ch[y][0]=0,pushup(y);
}
inline int read(){
register int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return (f==1)?x:-x;
}
int main()
{
n=read(),m=read();
for(int i=1;i<=n;i++){
val[i]=a[i]=read();
fa[i]=ch[i][0]=ch[i][1]=0;
}
int opt,x,y;
for(int i=1;i<=m;i++){
opt=read(),x=read(),y=read();
if(opt==0) split(x,y),printf("%d\n",val[y]);
if(opt==1) link(x,y);
if(opt==2) cut(x,y);
if(opt==3) splay(x),a[x]=y,pushup(x);
}
return 0;
}
2、[COCI 2009] OTOCI / 极地旅行社
其实就是 \(pushup\) 改了一下
\(Code\ Below:\)
#include <bits/stdc++.h>
using namespace std;
const int maxn=30000+10;
int n,q,a[maxn],ch[maxn][2],fa[maxn],val[maxn],sta[maxn],top;
bool rev[maxn];
inline int read(){
register int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return (f==1)?x:-x;
}
inline void reverse(int x){
swap(ch[x][0],ch[x][1]);
rev[x]^=1;
}
inline void pushup(int x){
val[x]=val[ch[x][0]]+val[ch[x][1]]+a[x];
}
inline void pushdown(int x){
if(rev[x]){
rev[x]=0;
if(ch[x][0]) reverse(ch[x][0]);
if(ch[x][1]) reverse(ch[x][1]);
}
}
inline bool nrt(int x){
return ch[fa[x]][0]==x||ch[fa[x]][1]==x;
}
inline void rotate(int x){
int y=fa[x],z=fa[y],k=(x==ch[y][1]),u=ch[x][k^1];
if(nrt(y)) ch[z][ch[z][1]==y]=x;
ch[y][k]=u;ch[x][k^1]=y;
if(u) fa[u]=y;fa[y]=x;fa[x]=z;
val[x]=val[y];pushup(y);
}
inline void splay(int x){
int y,z;top=0;
for(y=x;nrt(y);y=fa[y]) sta[top++]=y;
pushdown(y);
while(top) pushdown(sta[--top]);
while(nrt(x)){
y=fa[x],z=fa[y];
if(nrt(y)) rotate((ch[y][1]==x)^(ch[z][1]==y)?x:y);
rotate(x);
}
}
inline void access(int x){
int y=0;
for(;x;y=x,x=fa[x])
splay(x),ch[x][1]=y,pushup(x);
}
inline void makeroot(int x){
access(x),splay(x),reverse(x);
}
inline int findroot(int x){
access(x),splay(x);
for(;ch[x][0];x=ch[x][0]) pushdown(x);
return x;
}
inline void split(int x,int y){
makeroot(x),access(y),splay(y);
}
inline int link(int x,int y){
makeroot(x);
if(findroot(y)!=x){
fa[x]=y,pushup(y);
return 1;
}
return 0;
}
inline void cut(int x,int y){
makeroot(x);
if(findroot(y)==x&&fa[x]==y&&!ch[x][1])
fa[x]=ch[y][0]=0,pushup(y);
}
int main()
{
n=read();
for(int i=1;i<=n;i++)
val[i]=a[i]=read();
char opt[20];int x,y;
q=read();
for(int i=1;i<=q;i++){
scanf("%s",opt);
x=read(),y=read();
if(opt[0]=='b'){
if(link(x,y)) printf("yes\n");
else printf("no\n");
}
if(opt[0]=='p'){
splay(x),a[x]=y,pushup(x);
}
if(opt[0]=='e'){
makeroot(x);
if(findroot(y)!=x)
printf("impossible\n");
else split(x,y),printf("%d\n",val[y]);
}
}
return 0;
}
3、[WC2006]水管局长
删边操作不好处理,所以倒过来。
动态查询最大边权的最小值,先做一遍 \(Kruskal\),然后每次选一条最大的边权,如果当前加边的边权比最大的边权小,那么 \(Cut\) 掉原来的边然后 \(Link\) 一下现在的边。
边权不好处理,还要转化成点权,用虚点法。
\(Code\ Below:\)
#include <bits/stdc++.h>
#define pii pair<int,int>
using namespace std;
const int maxn=1000000+10;
int n,m,Q,ch[maxn][2],fa[maxn],val[maxn],Max[maxn],sta[maxn],f[maxn],ans[maxn],top;
bool rev[maxn],vis[maxn];
struct Edge{
int x,y,w;
}e[maxn];
struct Query{
int op,x,y,id;
}q[maxn];
map<pii,int> mp;
bool cmp(Edge a,Edge b){
return a.w<b.w;
}
inline int read(){
register int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return (f==1)?x:-x;
}
inline void reverse(int x){
swap(ch[x][0],ch[x][1]);
rev[x]^=1;
}
inline void pushup(int x){
Max[x]=x;
if(val[Max[ch[x][0]]]>val[Max[x]]) Max[x]=Max[ch[x][0]];
if(val[Max[ch[x][1]]]>val[Max[x]]) Max[x]=Max[ch[x][1]];
}
inline void pushdown(int x){
if(rev[x]){
rev[x]=0;
if(ch[x][0]) reverse(ch[x][0]);
if(ch[x][1]) reverse(ch[x][1]);
}
}
inline bool nrt(int x){
return x==ch[fa[x]][0]||x==ch[fa[x]][1];
}
inline void rotate(int x){
int y=fa[x],z=fa[y],k=(x==ch[y][1]),u=ch[x][k^1];
if(nrt(y)) ch[z][ch[z][1]==y]=x;
ch[y][k]=u;ch[x][k^1]=y;
if(u) fa[u]=y;fa[y]=x;fa[x]=z;
pushup(y);pushup(x);
}
inline void splay(int x){
int y,z;top=0;
for(y=x;nrt(y);y=fa[y]) sta[++top]=y;
pushdown(y);
while(top) pushdown(sta[top--]);
while(nrt(x)){
y=fa[x],z=fa[y];
if(nrt(y)) rotate((x==ch[y][1])^(y==ch[z][1])?x:y);
rotate(x);
}
}
inline void access(int x){
int y=0;
for(;x;y=x,x=fa[x])
splay(x),ch[x][1]=y,pushup(x);
}
inline void makeroot(int x){
access(x),splay(x),reverse(x);
}
inline int findroot(int x){
access(x),splay(x);
for(;ch[x][0];x=ch[x][0]) pushdown(x);
return x;
}
inline void split(int x,int y){
makeroot(x),access(y),splay(y);
}
inline void link(int x,int y){
makeroot(x);
if(findroot(y)!=x)
fa[x]=y,pushup(y);
}
inline void cut(int x,int y){
makeroot(x);
if(findroot(y)==x&&fa[x]==y&&!ch[y][1])
fa[x]=ch[y][0]=0,pushup(y);
}
int find(int x){
return (x==f[x])?x:f[x]=find(f[x]);
}
int main()
{
n=read(),m=read(),Q=read();
for(int i=1;i<=m;i++){
e[i].x=read(),e[i].y=read(),e[i].w=read();
if(e[i].x>e[i].y) swap(e[i].x,e[i].y);
}
sort(e+1,e+m+1,cmp);
for(int i=1;i<=m;i++){
mp[make_pair(e[i].x,e[i].y)]=i;
val[i+n]=e[i].w;Max[i+n]=i+n;
}
for(int i=1;i<=Q;i++){
q[i].op=read(),q[i].x=read(),q[i].y=read();
if(q[i].op==2){
if(q[i].x>q[i].y) swap(q[i].x,q[i].y);
q[i].id=mp[make_pair(q[i].x,q[i].y)];
vis[q[i].id]=1;
}
}
int x,y,t,a,b,tot=0;
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=m;i++){
if(!vis[i]){
x=e[i].x;y=e[i].y;
a=find(x);b=find(y);
if(a!=b){
link(x,i+n);link(y,i+n);
f[a]=b;tot++;
if(tot==n-1) break;
}
}
}
for(int i=Q;i>=1;i--){
x=q[i].x;y=q[i].y;
if(q[i].op==1){
split(x,y);ans[i]=val[Max[y]];
}
else {
split(x,y);t=Max[y];
if(val[q[i].id+n]<val[t]){
cut(e[t-n].x,t);cut(e[t-n].y,t);
link(x,q[i].id+n);link(y,q[i].id+n);
}
}
}
for(int i=1;i<=Q;i++)
if(q[i].op==1) printf("%d\n",ans[i]);
return 0;
}
4、[NOI2014]魔法森林
边权先按 \(b\) 排序,然后维护一个 \(a\) 的最大边权的最小值,把边权转换成点权。
\(Code\ Below:\)
// luogu-judger-enable-o2
#include <bits/stdc++.h>
using namespace std;
const int maxn=200000+10;
const int inf=0x3f3f3f3f;
int n,m,ch[maxn][2],fa[maxn],val[maxn],Max[maxn],sta[maxn],top,ans=inf;
bool rev[maxn];
struct Edge{
int x,y,a,b;
}e[maxn];
bool cmp(Edge x,Edge y){
return x.b<y.b;
}
inline int read(){
register int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return (f==1)?x:-x;
}
inline void reverse(int x){
swap(ch[x][0],ch[x][1]);
rev[x]^=1;
}
inline void pushup(int x){
Max[x]=x;
if(val[Max[ch[x][0]]]>val[Max[x]]) Max[x]=Max[ch[x][0]];
if(val[Max[ch[x][1]]]>val[Max[x]]) Max[x]=Max[ch[x][1]];
}
inline void pushdown(int x){
if(rev[x]){
rev[x]=0;
if(ch[x][0]) reverse(ch[x][0]);
if(ch[x][1]) reverse(ch[x][1]);
}
}
inline bool nrt(int x){
return x==ch[fa[x]][0]||x==ch[fa[x]][1];
}
inline void rotate(int x){
int y=fa[x],z=fa[y],k=(x==ch[y][1]),u=ch[x][k^1];
if(nrt(y)) ch[z][ch[z][1]==y]=x;
ch[y][k]=u;ch[x][k^1]=y;
if(u) fa[u]=y;fa[y]=x;fa[x]=z;
pushup(y);pushup(x);
}
inline void splay(int x){
int y,z;top=0;
for(y=x;nrt(y);y=fa[y]) sta[++top]=y;
pushdown(y);
while(top) pushdown(sta[top--]);
while(nrt(x)){
y=fa[x],z=fa[y];
if(nrt(y)) rotate((x==ch[y][1])^(y==ch[z][1])?x:y);
rotate(x);
}
}
inline void access(int x){
int y=0;
for(;x;y=x,x=fa[x])
splay(x),ch[x][1]=y,pushup(x);
}
inline int findroot(int x){
access(x),splay(x);
for(;ch[x][0];x=ch[x][0]) pushdown(x);
return x;
}
inline void makeroot(int x){
access(x),splay(x),reverse(x);
}
inline int split(int x,int y){
makeroot(x),access(y),splay(y);
return Max[y];
}
inline void link(int x,int y){
makeroot(x);
if(findroot(y)!=x)
fa[x]=y,pushup(y);
}
inline void cut(int x,int y){
makeroot(x);
if(findroot(y)==x&&fa[x]==y&&!ch[y][1])
fa[x]=ch[y][0]=0,pushup(y);
}
int main()
{
n=read(),m=read();
for(int i=1;i<=m;i++)
e[i].x=read(),e[i].y=read(),e[i].a=read(),e[i].b=read();
sort(e+1,e+m+1,cmp);
for(int i=1;i<=m;i++){
val[i+n]=e[i].a;
Max[i+n]=i+n;
}
int x,y,t;bool flag=0;
for(int i=1;i<=m;i++){
x=e[i].x;y=e[i].y;flag=1;
if(findroot(x)==findroot(y)){
t=split(x,y);
if(val[t]>e[i].a) cut(e[t-n].x,t),cut(e[t-n].y,t);
else flag=0;
}
if(flag) link(x,i+n),link(y,i+n);
if(findroot(1)==findroot(n)) ans=min(ans,e[i].b+val[split(1,n)]);
}
printf("%d\n",(ans==inf)?-1:ans);
return 0;
}