zoukankan      html  css  js  c++  java
  • BZOJ1969: [Ahoi2005]LANE 航线规划

    BZOJ1969: [Ahoi2005]LANE 航线规划

    Description

    对Samuel星球的探险已经取得了非常巨大的成就,于是科学家们将目光投向了Samuel星球所在的星系——一个巨大的由千百万星球构成的Samuel星系。
    星际空间站的Samuel II巨型计算机经过长期探测,已经锁定了Samuel星系中许多星球的空间坐标,并对这些星球从1开始编号1、2、3……。
    一些先遣飞船已经出发,在星球之间开辟探险航线。
    探险航线是双向的,例如从1号星球到3号星球开辟探险航线,那么从3号星球到1号星球也可以使用这条航线。
    例如下图所示:
      
    在5个星球之间,有5条探险航线。
    A、B两星球之间,如果某条航线不存在,就无法从A星球抵达B星球,我们则称这条航线为关键航线。
    显然上图中,1号与5号星球之间的关键航线有1条:即为4-5航线。
    然而,在宇宙中一些未知的磁暴和行星的冲撞,使得已有的某些航线被破坏,随着越来越多的航线被破坏,探险飞船又不能及时回复这些航线,可见两个星球之间的关键航线会越来越多。
    假设在上图中,航线4-2(从4号星球到2号星球)被破坏。
    此时,1号与5号星球之间的关键航线就有3条:1-3,3-4,4-5。
    小联的任务是,不断关注航线被破坏的情况,并随时给出两个星球之间的关键航线数目。
    现在请你帮助完成。

    Input

    第一行有两个整数N,M。表示有N个星球(1< N < 30000),初始时已经有M条航线(1 < M < 100000)。
    随后有M行,每行有两个不相同的整数A、B表示在星球A与B之间存在一条航线。
    接下来每行有三个整数C、A、B。
    C为1表示询问当前星球A和星球B之间有多少条关键航线;C为0表示在星球A和星球B之间的航线被破坏,当后面再遇到C为1的情况时,表示询问航线被破坏后,关键路径的情况,且航线破坏后不可恢复;
    C为-1表示输入文件结束,这时该行没有A,B的值。
    被破坏的航线数目与询问的次数总和不超过40000。

    Output

    对每个C为1的询问,输出一行一个整数表示关键航线数目。
    注意:我们保证无论航线如何被破坏,任意时刻任意两个星球都能够相互到达。
    在整个数据中,任意两个星球之间最多只可能存在一条直接的航线。

    Sample Input

    5 5
    1 2
    1 3
    3 4
    4 5
    4 2
    1 1 5
    0 4 2
    1 5 1
    -1

    Sample Output

    1
    3

    题解Here!
    网上一堆树链剖分套树状数组/线段树维护双连通分量,但是我不会啊。
    其实我是懒得写了。。。
    首先,时光倒流,把删边操作变成加边操作。
    这个套路了吧。。。
    然后用$LCT$维护两点间的桥边数量。
    首先缩点,变成一棵树。
    我们发现每次加边会形成一个环。
    将环缩点,也就是将环上的的点的父亲改为$Splay$重链的根。
    为什么这样是正确的呢?
    我们$makeroot(x)$之后,$x$在原树的子树就到了它的右子树,即$a[x].son[1]$。
    那么我们只需要把此时整个右子树加上$x$缩成一个点,即把它们在并查集中的父节点均变为$x$在并查集中的父节点。
    这样就是对的。
    至于怎么改,就暴力递归就好了。
    每次查询就是子树大小$-1$。
    然后是代码。
    $access(x)$操作魔改了一下。
    还有$findroot(x)$最后要$splay(x)$。
    附代码:
    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #define MAXN 200010
    using namespace std;
    int n,m,q=0;
    int fa[MAXN],ans[MAXN];
    int top=0,stack[MAXN];
    bool used[MAXN];
    struct Link_Cut_Tree{
        int son[2];
        int f,v,flag;
    }a[MAXN];
    struct Graph{
        int x,y;
        bool operator <(const Graph &p)const{
            return (x<p.x||(x==p.x&&y<p.y));
        }
    }b[MAXN];
    struct Question{
        int f,x,y;
    }que[MAXN];
    inline int read(){
    	int date=0,w=1;char c=0;
    	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    	return date*w;
    }
    int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    inline bool isroot(int rt){
        return a[a[rt].f].son[0]!=rt&&a[a[rt].f].son[1]!=rt;
    }
    inline void pushup(int rt){
        if(!rt)return;
        a[rt].v=a[a[rt].son[0]].v+a[a[rt].son[1]].v+1;
    }
    inline void pushdown(int rt){
        if(!rt||!a[rt].flag)return;
        a[a[rt].son[0]].flag^=1;a[a[rt].son[1]].flag^=1;a[rt].flag^=1;
        swap(a[rt].son[0],a[rt].son[1]);
    }
    inline void turn(int rt){
        int x=a[rt].f,y=a[x].f,k=a[x].son[0]==rt?1:0;
        if(!isroot(x)){
            if(a[y].son[0]==x)a[y].son[0]=rt;
            else a[y].son[1]=rt;
        }
        a[rt].f=y;a[x].f=rt;a[a[rt].son[k]].f=x;
        a[x].son[k^1]=a[rt].son[k];a[rt].son[k]=x;
        pushup(x);pushup(rt);
    }
    void splay(int rt){
        top=0;
        stack[++top]=rt;
        for(int i=rt;!isroot(i);i=a[i].f)stack[++top]=a[i].f;
        while(top)pushdown(stack[top--]);
        while(!isroot(rt)){
            int x=a[rt].f,y=a[x].f;
            if(!isroot(x)){
                if((a[y].son[0]==x)^(a[x].son[0]==rt))turn(rt);
                else turn(x);
            }
            turn(rt);
        }
    }
    inline void access(int rt){
        for(int i=0;rt;i=rt,rt=a[i].f=find(a[rt].f)){//这里魔改了一下,因为要维护换的父亲
            splay(rt);
            a[rt].son[1]=i;
            pushup(rt);
        }
    }
    inline void makeroot(int rt){access(rt);splay(rt);a[rt].flag^=1;}
    int findroot(int rt){
        access(rt);splay(rt);
        while(a[rt].son[0])rt=a[rt].son[0];
        splay(rt);//我也不知道
        return rt;
    }
    inline void split(int x,int y){makeroot(x);access(y);splay(y);}
    void change(int rt,int ancestry){
        if(!rt)return;
        fa[rt]=ancestry;
        if(a[rt].son[0])change(a[rt].son[0],ancestry);
        if(a[rt].son[1])change(a[rt].son[1],ancestry);
    }
    inline void link(int x,int y){
        if(x==y)return;
        makeroot(x);
        if(findroot(y)!=x){
            a[x].f=y;
            return;
        }
        change(a[x].son[1],x);
        a[x].son[1]=0;
        pushup(x);
    }
    inline int query(int x,int y){split(x,y);return a[y].v-1;}
    void work(){
        int x,y;
        for(int i=q;i>=1;i--){
            ans[i]=-1;
            x=find(que[i].x);y=find(que[i].y);
            if(que[i].f==0)link(x,y);
            else ans[i]=query(x,y);
        }
        for(int i=1;i<=q;i++)if(ans[i]!=-1)printf("%d
    ",ans[i]);
    }
    void init(){
        int x,y;
        n=read();m=read();
        for(int i=1;i<=n;i++)fa[i]=i;
        for(int i=1;i<=m;i++){
            b[i].x=read();b[i].y=read();
            used[i]=true;
            if(b[i].x>b[i].y)swap(b[i].x,b[i].y);
        }
        sort(b+1,b+m+1);
        while(1){
            int f=read();
            if(f==-1)break;
            q++;
            que[q].f=f;que[q].x=read();que[q].y=read();
            if(que[q].x>que[q].y)swap(que[q].x,que[q].y);
            if(que[q].f==0)used[lower_bound(b+1,b+m+1,(Graph){que[q].x,que[q].y})-b]=false;
        }
        for(int i=1;i<=m;i++)if(used[i])link(find(b[i].x),find(b[i].y));
    }
    int main(){
        init();
        work();
        return 0;
    }
    
  • 相关阅读:
    如何将PDF转换成word文档
    pdf转换成word教程
    VMware Net 模式网络配置
    多系统引导-refind
    Linux 磁盘分区调整工具
    Centos 8 安装 docker
    Centos 8 安装 gitlab13
    Centos 8 安装压缩版 mysql-8.0.21-el7-x86_64.tar.gz
    CentOS 添加用户并赋予管理员权限
    Centos 7 防火墙(firewall-cmd)添加端口访问
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9480528.html
Copyright © 2011-2022 走看看