# 【BZOJ4025】二分图（LCT动态维护图连通性）

### $LCT$动态维护图连通性

$LCT$动态维护树连通性，应该是比较简单，因为$LCT$本身就是一棵树，加边删边都很容易。

### 代码

#include<bits/stdc++.h>
#define Type template<typename I>
#define N 100000
#define M 200000
#define swap(x,y) (x^=y^=x^=y)
#define INF 1e9
using namespace std;
int n,m,t,flag,tag[N+M+5];
struct Operate
{
int x,y,pos,Begin,End;
}o1[M+5],o2[M+5];
class Class_FIO
{
private:
#define Fsize 100000
char ch,*A,*B,Fin[Fsize];
public:
Class_FIO() {A=B=Fin;}
Type inline void read(I& x) {x=0;while(!isdigit(ch=tc()));while(x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));}
}F;
class Class_LCT//LCT动态维护图连通性
{
private:
#define SIZE (N+M)
#define PushUp(x)\//上传子节点信息
(\
node[x].Size=node[node[x].Son[0]].Size+node[node[x].Son[1]].Size+(x>n),node[x].Min=x,\
Val[node[x].Min]>Val[node[node[x].Son[0]].Min]&&(node[x].Min=node[node[x].Son[0]].Min),\
Val[node[x].Min]>Val[node[node[x].Son[1]].Min]&&(node[x].Min=node[node[x].Son[1]].Min)\
)
#define Rever(x) (swap(node[x].Son[0],node[x].Son[1]),node[x].Rev^=1)
#define PushDown(x) (node[x].Rev&&(Rever(node[x].Son[0]),Rever(node[x].Son[1]),node[x].Rev=0))
#define Which(x) (node[node[x].Father].Son[1]==x)
#define Connect(x,y,d) (node[node[x].Father=y].Son[d]=x)
#define IsRoot(x) (node[node[x].Father].Son[0]^x&&node[node[x].Father].Son[1]^x)
#define MakeRoot(x) (Access(x),Splay(x),Rever(x))
#define Split(x,y) (MakeRoot(x),Access(y),Splay(y))
int Stack[SIZE+5];
struct Tree
{
int Min,Size,Rev,Father,Son[2];
}node[SIZE+5];
inline void Rotate(int x)
{
register int fa=node[x].Father,pa=node[fa].Father,d=Which(x);
!IsRoot(fa)&&(node[pa].Son[Which(fa)]=x),node[x].Father=pa,Connect(node[x].Son[d^1],fa,d),Connect(fa,x,d^1),PushUp(fa),PushUp(x);
}
inline void Splay(int x)
{
register int fa=x,Top=0;
while(Stack[++Top]=fa,!IsRoot(fa)) fa=node[fa].Father;
while(Top) PushDown(Stack[Top]),--Top;
while(!IsRoot(x)) fa=node[x].Father,!IsRoot(fa)&&(Rotate(Which(x)^Which(fa)?x:fa),0),Rotate(x);
}
inline void Access(int x) {for(register int son=0;x;x=node[son=x].Father) Splay(x),node[x].Son[1]=son,PushUp(x);}
public:
int Val[SIZE+5];
Class_LCT() {Val[0]=INF;}
inline void Init(int len) {for(register int i=1;i<=len;++i) Val[i]=INF,node[i].Min=i;}//将真正的点被删除时间设为INF
inline void Link(int x,int y) {MakeRoot(x),FindRoot(y)^x&&(node[x].Father=y);}
inline void Cut(int x,int y) {MakeRoot(x),!(FindRoot(y)^x)&&!(node[y].Father^x)&&!node[y].Son[0]&&(node[y].Father=node[x].Son[1]=0,PushUp(x));}
inline int FindRoot(int x)
{
Access(x),Splay(x);
while(node[x].Son[0]) PushDown(x),x=node[x].Son[0];
return Splay(x),x;
}
inline int QueryMin(int x,int y) {return Split(x,y),node[y].Min;}//查询子树内最早被删除的边的编号
inline int QuerySize(int x,int y) {return Split(x,y),node[y].Size;}//查询子树内有多少条边
#undef SIZE
}LCT;
inline bool cmp1(Operate x,Operate y) {return x.Begin<y.Begin;}//将边按出现时间排序
inline bool cmp2(Operate x,Operate y) {return x.End<y.End;}//将边按消失时间排序
{
register int x=o1[pos].x,y=o1[pos].y,z=o1[pos].pos;
if(!(x^y)) return (void)(tag[z]=1,++flag);//如果是自环，标记该边为形成奇环的边，并将计数器加1
register int p=LCT.QueryMin(x,y);//查询将形成环的这条链中最早被删掉的边的编号
if(LCT.Val[z]<LCT.Val[p]) return (void)(!(LCT.QuerySize(x,y)&1)&&(tag[z]=1,++flag));//如果当前插入的这条边先被删去，判断是否为奇环，然后退出函数
}
inline void Del(int pos)//删除一条边
{
register int x=o2[pos].x,y=o2[pos].y,z=o2[pos].pos;
if(tag[z]) return (void)(--flag);//如果这条边在一个奇环上，则将奇环个数减1
!(LCT.FindRoot(x)^LCT.FindRoot(z))&&!(LCT.FindRoot(y)^LCT.FindRoot(z))&&(LCT.Cut(x,z),LCT.Cut(z,y),0);//如果这条边还在图中，则将其删除
}
int main()
{
register int i,p1=1,p2=1;