[AHOI2005]航线规划——LCT维护边双联通分量

因为只能支持加入一个边维护边双,所以时光倒流

维护好边双,每次就是提取出(x,y)的链,答案就是链长度-1

具体维护边双的话,

void access(int x){
    for(reg y=0;x;y=x,x=t[y].fa=fin(t[x].fa)){//注意更新 splay(x);rs=y;pushup(x); } }

dele(int x,int y)把x节点的father指向y,这个x临死前把信息指到y,以便于后面要找x的直接找y即可。{

  if(x) fa[x]=y,dele(rs,y),dele(ls,y)
}

merge函数(int x,int y){

  if(x==y(在一起))return 什么都不用做

  makert(x)

  if(findrt(y)!=x){

    t[x].fa=y相当于直接link

    return;

  }

  splay(x)

  出环了。那么要缩点,x现在是根,并且作为代表点

  dele(rs,x)

  t[x].ch[1]=0,干掉子树,这个子树已经名存实亡了。

  pushup(x)

}

我通过merge,dele函数打上标记,

在access的时候把标记依次还原,达到真正缩点的目的。

而并查集使我每次都会到真正的节点。不会到已经删除的节点。

 

代码:

#include<bits/stdc++.h>
#define il inline
#define reg register int
#define numb (ch^'0')
#define ls t[x].ch[0]
#define rs t[x].ch[1]
using namespace std;
typedef long long ll;
il void rd(int &x){
    char ch;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
namespace Miracle{
const int N=30000+5;
const int M=100000+5;
int n,m;
struct node{
    int sz,fa,ch[2],r;
}t[N];
int fa[N];
int fin(int x){
    return fa[x]==x?x:fa[x]=fin(fa[x]);
}
bool nrt(int x){
    return (t[t[x].fa].ch[0]==x)||(t[t[x].fa].ch[1]==x);
}
void rev(int x){
    swap(t[x].ch[0],t[x].ch[1]);
    t[x].r^=1;    
}
void pushdown(int x){
    if(t[x].r){
        t[x].r=0;
        rev(ls);rev(rs);
    }
}
void pushup(int x){
    t[x].sz=t[ls].sz+t[rs].sz+1;
}
void rotate(int x){
    int y=t[x].fa,d=t[y].ch[1]==x;
    t[t[y].ch[d]=t[x].ch[!d]].fa=y;
    if(nrt(y)) t[t[x].fa=t[y].fa].ch[t[t[y].fa].ch[1]==y]=x;
    else t[x].fa=t[y].fa;
    t[t[y].fa=x].ch[!d]=y;
    pushup(y);
}
int sta[N];
void splay(int x){
    int y=x,z=0;
    sta[++z]=y;
    while(nrt(y)) y=t[y].fa,sta[++z]=y;
    while(z) pushdown(sta[z--]);
    
    while(nrt(x)){
        y=t[x].fa,z=t[y].fa;
        if(nrt(y)){
            rotate(((t[y].ch[0]==x)==(t[z].ch[0]==y))?y:x);            
        }
        rotate(x);
    }
    pushup(x);
}
void access(int x){
    for(reg y=0;x;y=x,x=t[y].fa=fin(t[x].fa)){
        splay(x);rs=y;pushup(x);
    }
}
void makert(int x){
    access(x);splay(x);rev(x);
}
int findrt(int x){
    access(x);splay(x);
    pushdown(x);
    while(t[x].ch[0]) pushdown(x=t[x].ch[0]);
    splay(x);
    return x;
}
void dele(int x,int y){
    if(x) fa[x]=y,dele(ls,y),dele(rs,y);
}
void merge(int x,int y){
    if(x==y) return;
    makert(x);
    if(findrt(y)!=x){
        t[x].fa=y;
        pushup(y);
        return;
    }
    dele(t[x].ch[1],x);
    t[x].ch[1]=0;
    pushup(x);
}
void split(int x,int y){
    makert(x);access(y);splay(y);
}
struct edge{
    int x,y;
    bool friend operator <(edge a,edge b){
        if(a.x==b.x) return a.y<b.y;
        return a.x<b.x;
    }
}e[M];
bool vis[M];
struct que{
    int c;
    int x,y;
}q[M];
int ans[M],cnt;
int main(){
    rd(n);rd(m);
    int x,y;    
    for(reg i=1;i<=n;++i){
        fa[i]=i;t[i].sz=1;
    }
    for(reg i=1;i<=m;++i){
        rd(x);rd(y);
        if(x>y) swap(x,y);
        e[i].x=x;e[i].y=y;
    }
    sort(e+1,e+m+1);
    int c;
    int tot=0;
    while(1){
        rd(c);
        if(c==-1) break;
        ++tot;
        rd(x);rd(y);
        if(x>y) swap(x,y);
        if(c==0){
            edge lp;lp.x=x,lp.y=y;
            vis[lower_bound(e+1,e+m+1,lp)-e]=1;
        }
        q[tot].x=x,q[tot].y=y;
        q[tot].c=c;
    }

    for(reg i=1;i<=m;++i){
        if(!vis[i]){
            x=fin(e[i].x),y=fin(e[i].y);
            merge(x,y);
        }
    }
    for(reg i=tot;i>=1;--i){
        x=fin(q[i].x);y=fin(q[i].y);
        if(q[i].c==1){
            ++cnt;
            split(x,y);
            ans[cnt]=t[y].sz-1;
        }
        else{
            merge(x,y);
        }
    }
    for(reg i=cnt;i>=1;--i){
        printf("%d\n",ans[i]);
    }
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2018/12/19 9:57:28
*/

 

转载于:https://www.cnblogs.com/Miracevin/p/10142329.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值