bzoj4998 星球联盟

这篇博客讨论了一个关于星球联盟的问题,其中涉及到如何通过建立太空隧道来连接星球并判断是否形成联盟。文章提出了利用并查集和LCT数据结构来解决这个问题,特别是在新隧道建成时判断星球是否属于同一联盟并计算联盟星球数。示例输入和输出展示了算法的应用情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://www.elijahqi.win/2018/02/15/bzoj4998/
Description
在遥远的S星系中一共有N个星球,编号为1…N。其中的一些星球决定组成联盟,以方便相互间的交流。但是,组成
联盟的首要条件就是交通条件。初始时,在这N个星球间有M条太空隧道。每条太空隧道连接两个星球,使得它们能
够相互到达。若两个星球属于同一个联盟,则必须存在一条环形线路经过这两个星球,即两个星球间存在两条没有
公共隧道的路径。为了壮大联盟的队伍,这些星球将建设P条新的太空隧道。这P条新隧道将按顺序依次建成。一条
新轨道建成后,可能会使一些星球属于同一个联盟。你的任务是计算出,在一条新隧道建设完毕后,判断这条新轨
道连接的两个星球是否属于同一个联盟,如果属于同一个联盟就计算出这个联盟中有多少个星球。
Input
第1行三个整数N,M和P,分别表示总星球数,初始时太空隧道的数目和即将建设的轨道数目。
第2至第M+1行,每行两个整数,表示初始时的每条太空隧道连接的两个星球编号。
第M+2行至第M+P+1行,每行两个整数,表示新建的太空隧道连接的两个星球编号。
这些太空隧道按照输入的顺序依次建成。
1≤N,M,P≤200000
Output
输出共P行。
如果这条新的太空隧道连接的两个星球属于同一个联盟,就输出一个整数,表示这两个星球所在联盟的星球数。
如果这条新的太空隧道连接的两个星球不属于同一个联盟,就输出”No”(不含引号)。
Sample Input
5 3 4
1 2
4 3
4 5
2 3
1 3
4 5
2 4
Sample Output
No
3
2
5
HINT
相当于每次加边 如果形成了联通块 则输出连通块的块的个数是多少 连通块的意思是说边双联通分量的意思
因为只有加边操作可以考虑用并查集维护
fa1 存连通性 fa2存是否属于同一个点 那么我每次加边的时候首先判断下当前这两个点是否在同一个连通块内 如果在 直接返回答案即可 如果不在则判断下这两个是否已经连通 没有连通的话就用lct link一下 如果已经连通了 那么说明经过我这次操作可以让我变成边双 那么每当这时我就取dfs一下统计答案即可 同时把并查集 的fa2都指向我当前这个集合的头即可 注意因为我要删点 所以在做任何操作的时候我都需要用并查集 找到我这个集合的头 然后往上跳才可以
具体地,对于每个加边操作,如果它们不在同一个边双里且未连通,则把它们所在边双连上。否则如果它们不在同一个边双里且已经连通,则需要提取它们之间的路径,把路径上的点所在边双全部改为新的边双并在LCT中“删除”这些点。这个过程可以直接对Splay Tree进行dfs实现,并使用并查集来维护。

#include<cstdio>
#include<algorithm>
#define N 220000
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc();}
    while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=gc();
    return x*f;
}
//fa1 存连通性 fa2存是否属于同一个点 
int fa[N],fa1[N],fa2[N],rev[N],c[N][2],n,m,v[N],p,top,q[N];
inline int find1(int x){
    while(x!=fa1[x]) x=fa1[x]=fa1[fa1[x]];return x;
}
inline int find2(int x){
    while(x!=fa2[x]) x=fa2[x]=fa2[fa2[x]];return x;
}
inline void pushdown(int x){
    if (!rev[x]) return;rev[x]=0;
    int l=c[x][0],r=c[x][1];rev[l]^=1;
    swap(c[x][0],c[x][1]);rev[r]^=1;
}
inline bool isroot(int x){
    return c[find2(fa[x])][0]!=x&&c[find2(fa[x])][1]!=x;
}
inline void rotate(int x){
    int y=find2(fa[x]),z=find2(fa[y]);
    if (!isroot(y)) c[z][c[z][1]==y]=x;
    int l=c[y][1]==x,r=l^1;
    fa[c[x][r]]=y;fa[y]=x;fa[x]=z;
    c[y][l]=c[x][r];c[x][r]=y;
}
inline void splay(int x){
    q[top=1]=x;for (int i=x;!isroot(i);i=find2(fa[i])) q[++top]=fa[i];
    while(top) pushdown(q[top--]);
    while(!isroot(x)){
        int y=find2(fa[x]),z=find2(fa[y]);
        if (!isroot(y)){
            if (c[y][0]==x^c[z][0]==y) rotate(x);else rotate(y);
        }rotate(x);
    }
}
inline void access(int x){
    for (int t=0;x;t=x,x=find2(fa[x])) splay(x),c[x][1]=t;
}
inline void makeroot(int x){
    access(x);splay(x);rev[x]^=1;
}
inline void link(int x,int y){
    makeroot(x);fa[x]=y;
    fa1[find1(x)]=find1(y);
}
inline void dfs(int x,int f){
    if (!x) return;fa2[x]=f;v[f]+=v[x];dfs(c[x][0],f);dfs(c[x][1],f);
}
inline int add(int x,int y){
    x=find2(x);y=find2(y);
    if (x==y) return v[x];
    if (find1(x)!=find1(y)) {link(x,y);return 0;}
    makeroot(x);access(y);splay(y);dfs(c[y][0],y);return v[y];
}
int main(){
    freopen("bzoj4998.in","r",stdin);
    n=read();m=read();p=read();
    for (int i=1;i<=n;++i) fa1[i]=fa2[i]=i,v[i]=1;
    for (int i=1;i<=m;++i){int x=read(),y=read();add(x,y);}
    //for (int i=1;i<=n;++i) printf("%d %d\n",fa1[i],fa2[i]);
    for (int i=1;i<=p;++i){
        int x=read(),y=read();
        int t=add(x,y);
        if (!t) puts("No");else printf("%d\n",t);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值