版权声明: https://blog.csdn.net/qq_16267919/article/details/81659972
洛谷P2147 [SDOI2008]洞穴勘测
LCT用来维护若干棵无根树,使用伸展树来实现.每棵伸展树维护原树上的一条重链,一棵伸展树中的根节点(非原树的根)的父亲单向指向另一棵伸展树中的它原树中该重链起始点的父亲节点.
struct lct
{
data tr[maxn];
void init()
{
LL i;
fo(i,0,n)
{
tr[i]=data();
tr[i].key=a[i];
}
}
bool isrt(LL x)
{
return (!tr[x].f) || (tr[tr[x].f][0]!=x && tr[tr[x].f][1]!=x);
}
void up(LL x)
{
tr[x].ma=tr[x].key;
if (tr[x][0]) cmax(tr[x].ma,tr[tr[x][0]].ma);
if (tr[x][1]) cmax(tr[x].ma,tr[tr[x][1]].ma);
}
void rev(LL x)
{
swap(tr[x][0],tr[x][1]); tr[x].tag^=1;
}
void pd(LL x)
{
if (!tr[x].tag) return;
if (tr[x][0]) rev(tr[x][0]);
if (tr[x][1]) rev(tr[x][1]);
tr[x].tag=0;
}
void cl(LL x)//从根开始下放标记
{
LL top=1,i=x;
temp[top]=x;
while (!isrt(i)) temp[++top]=i=tr[i].f;
while (top) pd(temp[top--]);
}
void rotate(LL x)
{
LL f=tr[x].f,k=gs(x),ff=tr[f].f;
tr[f][k]=tr[x][k^1]; if (tr[x][k^1]) tr[tr[x][k^1]].f=f;
tr[x][k^1]=f; tr[f].f=x;
tr[x].f=ff;
if (tr[ff][0]==f) tr[ff][0]=x;
if (tr[ff][1]==f) tr[ff][1]=x;
up(f); up(x);
}
void splay(LL x)
{
cl(x);
for(;!isrt(x);rotate(x))
if (!isrt(tr[x].f) && gs(x)==gs(tr[x].f)) rotate(tr[x].f);
}
void access(LL x)
{
for(LL y=0;x;y=x,x=tr[x].f)
{
splay(x);//把x转到根
tr[x][1]=y;//原树深度比x大1的点替换为含有操作节点的y
up(x);//记得更新
}
}
void make_root(LL x)
{
access(x);//让x和根在一棵伸展树中
splay(x);//把x转到根
rev(x);//把x到根的边反向
}
LL find_root(LL x)
{
while (tr[x].f) x=tr[x].f;
return x;
}
void link(LL u,LL v)
{
if (find_root(u)==find_root(v)) return;
make_root(u);
tr[u].f=v;
access(u);
}
void cut(LL u,LL v)
{
if (find_root(u)!=find_root(v)) return;
make_root(u);
access(v);
splay(v);
if (tr[v][0]!=u || tr[u].f==v || tr[u][1]) return; //判断u,v之间是否有之间相连的边
tr[v][0]=0; tr[u].f=0;
up(v);
}
LL ask(LL u,LL v)
{
if (find_root(u)!=find_root(v)) return -1;
make_root(u);
access(v);
splay(u);
return tr[u].ma;
}
}lct[maxc];
P2387 [NOI2014]魔法森林
枚举a,lct维护以b为关键字的最小生成树,把每条边看做一个点,向它连接的两个点连边,加边时若两点已联通,则判断两点路径上最大边权是否大于该边边权,若是则删除最大边,加入该边。
Code
bzoj4025 二分图
维护以删除时间为关键字的最大生成树,加边时若两点联通则判断是否能替换路径上边权最小边,否则计入数组中并增加奇环数目,删除时若删除数组中的边则减少奇环数目。
Code