zoukankan      html  css  js  c++  java
  • [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
    ",ans[i]);
        }
        return 0;
    }
    
    }
    signed main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2018/12/19 9:57:28
    */
  • 相关阅读:
    MySQL缓存的查询和清除命令使用详解
    Mysql索引学习笔记
    Java使用BufferedImage修改图片内容
    Vue.js学习
    Linux一键安装宝塔控制面板
    SpringAop注解实现日志的存储
    Spring + SpringMVC + Mybatis项目中redis的配置及使用
    Java 常见异常种类
    6.28笔记-servlet3.0注解配置、文件上传、过滤器、监听器
    6.27-JSTL、标签、分页
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10142329.html
Copyright © 2011-2022 走看看