【jzoj3875】【星球联盟】【树】【并查集】

本文介绍了一种解决特定图论问题的算法:在S星系中构建联盟,通过增加新太空隧道判断是否形成有效联盟及其规模。使用并查集进行高效节点连接验证。

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

题目大意

在遥远的S星系中一共有N个星球,编号为1…N。其中的一些星球决定组成联盟,以方便相互间的交流。但是,组成联盟的首要条件就是交通条件。初始时,在这N个星球间有M条太空隧道。每条太空隧道连接两个星球,使得它们能够相互到达。若两个星球属于同一个联盟,则必须存在一条环形线路经过这两个星球,即两个星球间存在两条没有公共隧道的路径。为了壮大联盟的队伍,这些星球将建设P条新的太空隧道。这P条新隧道将按顺序依次建成。一条新轨道建成后,可能会使一些星球属于同一个联盟。你的任务是计算出,在一条新隧道建设完毕后,判断这条新轨道连接的两个星球是否属于同一个联盟,如果属于同一个联盟就计算出这个联盟中有多少个星球。

解题思路

离线所有询问,将最早出现的边构成树,其他的先存起来,用询问边构成树边的打no的标记。将给出的边构成的环用并查集并起来,暴力并即可,深度大的先并,因为只会并一次,维护集合和点数即可。

code

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LF double
#define LL long long
#define min(a,b) ((a<b)?a:b)
#define max(a,b) ((a>b)?a:b)
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
int const maxn=2*1e5,inf=1e9;
int n,m,p,fa[maxn+10][10],num[maxn+10][10],gra,to[maxn*2+10],next[maxn*2+10],
begin[maxn+10],f[maxn+10],tag[maxn+10],u[maxn+10],v[maxn+10],uu[maxn+10],vis[maxn+10],
vv[maxn+10],dep[maxn+10];
int get(int x,int p){
    if(!fa[x][p])return x;
    return fa[x][p]=get(fa[x][p],p);
}
int connect(int x,int y,int p){
    int fau=get(x,p),fav=get(y,p);
    if(fau==fav)return 0;
    fa[fau][p]=fav;
    num[fav][p]+=num[fau][p];
    return 1;
}
void insert(int x,int y){
    to[++gra]=y;
    next[gra]=begin[x];
    begin[x]=gra;
}
void dfs(int now,int pre){
    vis[now]=1;
    for(int i=begin[now];i;i=next[i])if(to[i]!=pre){
        f[to[i]]=now;dep[to[i]]=dep[now]+1;
        dfs(to[i],now);
    }
}
int main(){
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    scanf("%d%d%d",&n,&m,&p);
    fo(i,1,n)num[i][0]=num[i][1]=1;int cnt=0,tmppp=0;
    fo(i,1,m){
        scanf("%d%d",&u[i],&v[i]);
        if(connect(u[i],v[i],0)){insert(u[i],v[i]),insert(v[i],u[i]);tmppp++;}
        else uu[++cnt]=u[i],vv[cnt]=v[i];
    }
    fo(i,1,p){
        scanf("%d%d",&u[i],&v[i]);
        if(connect(u[i],v[i],0)){
            tag[i]=1;tmppp++;
            insert(u[i],v[i]);insert(v[i],u[i]);
        }
    }
    fo(i,1,n)if(!vis[i]){
        dep[i]=1;dfs(i,0);
    }

    fo(i,1,cnt){
        int fau=uu[i],fav=vv[i],tmp,tmpp=0;
        while((fau=get(fau,1))!=(fav=get(fav,1))){
            if(dep[fau]<dep[fav])swap(fau,fav);
            connect(fau,f[fau],1);
            //if((fau==get(fau,1))&&(fav==get(fav,1)))tmpp++;
            //if(tmpp>10)break;
        }
    }
    fo(i,1,p)if(tag[i])printf("No\n");else{
        int fau=u[i],fav=v[i],tmp,tmpp=0;;
        while((fau=get(fau,1))!=(fav=get(fav,1))){
            if(dep[fau]<dep[fav])swap(fau,fav);
            connect(fau,f[fau],1);
            //if((fau==get(fau,1))&&(fav==get(fav,1)))tmpp++;
            //if(tmpp>10)break;
        }
        printf("%d\n",num[get(fau,1)][1]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值