JZOJ 3875. 【NOIP2014八校联考第4场第2试10.20】星球联盟(alliance)

Description

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

Input

第1行三个整数N,M和P,分别表示总星球数,初始时太空隧道的数目和即将建设的轨道数目。
第2至第M+1行,每行两个整数,表示初始时的每条太空隧道连接的两个星球编号。
第M+2行至第M+P+1行,每行两个整数,表示新建的太空隧道连接的两个星球编号。这些太空隧道按照输入的顺序依次建成。

Output

输出共P行。如果这条新的太空隧道连接的两个星球属于同一个联盟,就输出一个整数,表示这两个星球所在联盟的星球数。如果这条新的太空隧道连接的两个星球不属于同一个联盟,就输出”No”(不含引号)。

Sample Input

输入1:

3 2 1
1 2
1 3
2 3

输入2:

5 3 4
1 2
4 3
4 5
2 3
1 3
4 5
2 4

Sample Output

输出1:

3

输出2:

No
3
2
5

Data Constraint

对于 10% 的数据有 1N,M,P100
对于 40% 的数据有 1N,M,P2000
对于 100% 的数据有 1N,M,P200000

Hint

Hint

Solution

  • 这一题是运用并查集的典例,使用灵活。

  • 首先对所有边逐一处理,如果连了边之后不会形成环,就直接连边,否则打一个标记(不连)

  • 其中可以用并查集维护两个端点是否属于同一个集合

  • 以后处理出森林的每个点的父亲节点和深度

  • 然后就开始处理未连的边,对于两个端点,

  • 像找最近公共祖先一样,逐个逐个往上走,途中维护累加环的大小即可

  • 总时间复杂度 O(M+P)

Code

#include<cstdio>
using namespace std;
const int N=500001;
struct data
{
    int x,y;
}edge[N];
int tot,num,f1,f2;
int f[N],g[N];
int fa[N],dep[N];
int first[N],next[N*4],en[N*4];
bool bz[N];
inline int read()
{
    int data=0; char ch=0;
    while(ch<'0' || ch>'9') ch=getchar();
    while(ch>='0' && ch<='9') data=data*10+ch-'0',ch=getchar();
    return data;
}
inline int get(int x)
{
    return (f[x]==x)?x:f[x]=get(f[x]);
}
inline void insert(int x,int y)
{
    next[++tot]=first[x];
    first[x]=tot;
    en[tot]=y;
}
inline void dfs(int x,int y)
{
    dep[x]=dep[fa[x]=y]+1;
    for(int i=first[x];i;i=next[i])
        if(en[i]!=y) dfs(en[i],x);
}
inline void work(int v)
{
    f1=edge[v].x,f2=edge[v].y;
    while(true)
    {
        f1=get(f1),f2=get(f2);
        if(f1==f2) return;
        if(dep[f1]>dep[f2])
        {
            g[f[f1]=f2]+=g[f1];
            g[f1]=0;
            f1=fa[f1];
        }else
        {
            g[f[f2]=f1]+=g[f2];
            g[f2]=0;
            f2=fa[f2];
        }
    }
}
int main()
{
    int n=read(),m=read(),p=read();
    for(int i=1;i<=n;i++) f[i]=i;
    for(int i=1;i<=m+p;i++)
    {
        f1=get(edge[i].x=read());
        f2=get(edge[i].y=read());
        if(f1!=f2)
        {
            f[f1]=f2;
            insert(edge[i].x,edge[i].y);
            insert(edge[i].y,edge[i].x);
            bz[i]=true;
        }
    }
    for(int i=1;i<=n;i++)
        if(!dep[i]) dfs(i,0);
    for(int i=1;i<=n;i++) g[f[i]=i]=1;
    for(int i=1;i<=m;i++)
        if(!bz[i]) work(i);
    for(int i=m+1;i<=m+p;i++)
        if(!bz[i])
        {
            work(i);
            printf("%d\n",g[f1]);
        }else printf("No\n");
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值