Luogu4172 [WC2006]水管局长
https://www.luogu.com.cn/problem/P4172
\(LCT\)动态维护最小生成树裸题,但是我用了指针版\(LCT\),\(RE\)到飞起
考虑这道题是删边,我们可以改成加边进行操作,边权仍然可以按照套路链一个点当做边
为了防止访问空指针,我们可以把根节点的父亲指向一个结构体\(rt\)
注意,那些会改变原指针的函数不能使用\(*\&x\),只能用\(*x\),否则会把原来的指针指向变掉
这次\(findroot\)写挂了,下次小心点
当然\(connect,update,push\_rev\)等处都需要特判空指针
\(update:\)本题使蒟蒻对指针恨之入骨,下次写数据结构如果用指针并不比结构体方便,蒟蒻可就要把指针束之高阁了!
\(Code:\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<tr1/unordered_map>
#define ls ch[0]
#define rs ch[1]
#define N 101005
using namespace std;
using namespace std :: tr1;
unordered_map<int,int>E[N];
int n,m,Q,opt,x,y;
int e[N][3];
int ans[N];
struct node
{
int opt,u,v;
}que[N];
struct LCT
{
LCT *ch[2],*f=NULL,*Mid,*lef,*rig;
int s,w;
bool rev;
void clear()
{
ls=rs=Mid=lef=rig=NULL;
s=w=0,rev=false;
}
}rt,a[N],*q[N];
LCT *cnt,*g[N];
void connect(LCT *&x,LCT *&y,int son)
{
if (!x)
{
y->ch[son]=NULL;
return;
}
x->f=y;
y->ch[son]=x;
}
bool isrt(LCT *&x)
{
return x->f->ls!=x && x->f->rs!=x;
}
int id(LCT *&x)
{
return (x->f->ls==x)?0:1;
}
void update(LCT *&x)
{
x->s=x->w,x->Mid=x;
if (x->ls && x->ls->s>x->s)
x->s=x->ls->s,x->Mid=x->ls->Mid;
if (x->rs && x->rs->s>x->s)
x->s=x->rs->s,x->Mid=x->rs->Mid;
}
void rot(LCT *&x)
{
LCT *y=x->f,*r=y->f;
int yson=id(x),rson=id(y);
if (isrt(y))
x->f=r; else
connect(x,r,rson);
connect(x->ch[yson^1],y,yson);
connect(y,x,yson^1);
update(y),update(x);
}
void push_rev(LCT *&x)
{
if (!x)
return;
swap(x->ls,x->rs);
x->rev=!x->rev;
}
void push_down(LCT *&x)
{
if (x->rev)
{
push_rev(x->ls),push_rev(x->rs);
x->rev=false;
}
}
void splay(LCT *&x)
{
LCT *g=x;
int k=0;
q[++k]=g;
while (!isrt(g))
{
g=g->f,q[++k]=g;
}
while (k)
push_down(q[k--]);
while (!isrt(x))
{
LCT *y=x->f;
if (isrt(y))
rot(x); else
if (id(x)==id(y))
rot(y),rot(x); else
rot(x),rot(x);
}
}
void access(LCT *x)
{
for (LCT *y=NULL;x!=(&rt);y=x,x=x->f)
{
splay(x);
x->rs=y;
update(x);
}
}
void makeroot(LCT *&x)
{
access(x);
splay(x);
push_rev(x);
}
void split(LCT *&x,LCT *&y)
{
makeroot(x);
access(y);
splay(y);
}
LCT* findroot(LCT *x)
{
access(x),splay(x);
push_down(x);
while (x->ls)
{
x=x->ls;
push_down(x);
}
return x;
}
void link(LCT *&x,LCT *&y)
{
makeroot(x);
x->f=y;
}
void cut(LCT *&x,LCT *&y)
{
makeroot(x);
access(y);
splay(y);
x->f=&rt,y->ls=NULL;
update(y);
}
void link_MST(LCT *&x,LCT *&y,int z)
{
if (findroot(x)!=findroot(y))
{
++cnt;
cnt->w=z,cnt->f=&rt;
cnt->lef=x,cnt->rig=y;
link(x,cnt);
link(y,cnt);
} else
{
split(x,y);
if (y->s>z)
{
LCT *ct=y->Mid;
cut(ct,ct->lef),cut(ct,ct->rig);
ct->clear();
ct->w=z,ct->f=&rt;
ct->lef=x,ct->rig=y;
link(ct,x);
link(ct,y);
}
}
}
int main()
{
scanf("%d%d%d",&n,&m,&Q);
rt.w=-1;
for (int i=1;i<=n;i++)
g[i]=a+i,g[i]->w=0,g[i]->f=&rt;
cnt=g[n];
for (int i=1;i<=m;i++)
scanf("%d%d%d",&e[i][0],&e[i][1],&e[i][2]),E[e[i][0]][e[i][1]]=E[e[i][1]][e[i][0]]=i;
for (int i=1;i<=Q;i++)
{
scanf("%d%d%d",&que[i].opt,&que[i].u,&que[i].v);
if (que[i].opt==2)
E[que[i].u][que[i].v]=E[que[i].v][que[i].u]=-E[que[i].v][que[i].u];
}
for (int i=1;i<=m;i++)
if (E[e[i][0]][e[i][1]]>0)
link_MST(g[e[i][0]],g[e[i][1]],e[i][2]);
for (int i=Q;i;i--)
{
int opt=que[i].opt,x=que[i].u,y=que[i].v;
if (opt==1)
{
split(g[x],g[y]);
ans[++ans[0]]=g[y]->s;
} else
link_MST(g[x],g[y],e[-E[x][y]][2]);
}
for (int i=ans[0];i;i--)
printf("%d\n",ans[i]);
return 0;
}
观察这部分代码(在函数\(link\_MST\))
if (y->s>z)
{
LCT *ct=y->Mid;
cut(ct,ct->lef),cut(ct,ct->rig);
ct->clear();
ct->w=z,ct->f=&rt;
ct->lef=x,ct->rig=y;
link(ct,x);
link(ct,y);
}
由于我们建的特殊点度数为\(2\),那么我们将\(x,y\)这条链抽出来之后,一定包含所要断掉的特殊点两个相连的点,我们将该特殊点\(Splay\)到根,直接让它与左右儿子分离即可
所以\(cut\)可以去掉,两个相连的点也无需记录,改变为以下这部分代码
if (y->s>z)
{
LCT *ct=y->Mid;
splay(ct);
if (ct->ls)
ct->ls->f=&rt;
if (ct->rs)
ct->rs->f=&rt;
ct->clear();
ct->w=z,ct->f=&rt;
link(ct,x);
link(ct,y);
}