太弱了不會樹剖,覺得LCT好寫一些,就上LCT亂搞,當LCT維護雙連通分量的練手題好了
正序刪邊是不好來維護連通性的,於是就像水管局長那樣離線處理,逆序完成操作
顯然,每個點可以代表一個雙連通分量,查詢就是鏈的長度-1
連接一條邊,如果在LCT中還沒連通就link,如果連通了,顯然這里會出現一個環,然后暴力縮點,可以把當前輔助樹的根節點當做集合的標志節點,然后dfs整個輔助樹,把鏈上的其它點的並查集祖先暴力改成這個標志節點,最后再斷開標志節點與子樹的連接。總的暴力修改次數不會超過\(N\log N\)次,復雜度是對的
但是點縮完了,那它們的子樹不會指空嗎?所以,access的時候,要更新\(x\)為\(geth(f[x])\)
至於常數,LCT也並不是很慢啊。反正有了O2還是可以做到很優秀的
代碼細節很多,調試真心累TAT
#include<cstdio>
#include<algorithm>
using namespace std;
#define RG register
#define R RG short
#define I inline void
#define IB inline bool
#define IS inline short
#define G ch=getchar()
#define lc c[x][0]
#define rc c[x][1]
const int N=30009,M=100009;
short f[N],c[N][2],s[N],h[N],a[N],b[N],op[N],ans[M];
bool r[N],vis[M];
struct EDGE{//對邊排序,方便查找該邊是否被刪除
short x,y;
IB operator<(const EDGE a)const{
return x<a.x||(x==a.x&&y<a.y);
}
}e[M];
template<typename T>
I in(RG T&z){
RG char G;
while(ch<'-')G;
z=ch&15;G;
while(ch>'-')z*=10,z+=ch&15,G;
}
IS geth(R x){
if(x==h[x])return x;
return h[x]=geth(h[x]);
}
IB nroot(R x){
return c[f[x]][0]==x||c[f[x]][1]==x;
}
I pushup(R x){
s[x]=s[lc]+s[rc]+1;
}
I pushdown(R x){
if(r[x]){
swap(lc,rc);
r[lc]^=1;r[rc]^=1;r[x]=0;
}
}
I pushall(R x){
if(nroot(x))pushall(f[x]);
pushdown(x);
}
I rotate(R x){
R y=f[x],z=f[y],k=c[y][1]==x,w=c[x][!k];
if(nroot(y))c[z][c[z][1]==y]=x;f[x]=z;
c[x][!k]=y;f[y]=x;
c[y][k]=w;f[w]=y;
pushup(y);
}
I splay(R x){
pushall(x);
R y;
while(nroot(x)){
if(nroot(y=f[x]))
rotate((c[f[y]][0]==y)^(c[y][0]==x)?x:y);
rotate(x);
}
pushup(x);
}
I access(R x){
for(R y=0;x;y=x,x=f[y]=geth(f[x]))//注意更新
splay(x),rc=y,pushup(x);
}
I makeroot(R x){
access(x);splay(x);
r[x]^=1;
}
IS findroot(R x){
access(x);splay(x);
pushdown(x);
while(lc)pushdown(x=lc);
splay(x);
return x;
}
I split(R x,R y){
makeroot(y);
access(x);splay(x);
}
I del(R x,R y){//函數遞歸縮點
if(x)h[x]=y,del(lc,y),del(rc,y);
}
I merge(R x,R y){
if(x==y)return;//在一個分量里什么都不用干
makeroot(x);
if(findroot(y)!=x){
f[x]=y;return;//等於link
}
del(rc,x);
rc=0;pushup(x);//縮點,刪點
}
int main(){
RG int n,m,i,j;
R x,y;
in(n);in(m);
for(i=1;i<=n;++i)s[i]=1,h[i]=i;
for(i=1;i<=m;++i){
in(x);in(y);
if(x>y)swap(x,y);//強制編號,方便以后查找
e[i]=(EDGE){x,y};
}
sort(e+1,e+m+1);
for(j=1;in(op[j]),op[j]!=131;++j){
in(x);in(y);
if(!op[j]){
if(x>y)swap(x,y);
vis[lower_bound(e+1,e+m+1,(EDGE){x,y})-e]=1;
}//重載完小於號,直接二分找到,再打上刪除記號
a[j]=x;b[j]=y;
}
for(i=1;i<=m;++i)
if(!vis[i])merge(geth(e[i].x),geth(e[i].y));
for(i=0,--j;j;--j){
x=geth(a[j]);y=geth(b[j]);
if(op[j])split(x,y),ans[++i]=s[x]-1;
else merge(x,y);
}
while(i)printf("%hd\n",ans[i--]);
return 0;
}