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\n",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;
}

 

posted @ 2018-08-15 11:41  符拉迪沃斯托克  阅读(226)  评论(0编辑  收藏  举报
Live2D