星球联盟

8 篇文章 0 订阅
4 篇文章 0 订阅

题目描述
在遥远的S星系中一共有N个星球,编号为1…N。其中的一些星球决定组成联盟,以方便相互间的交流。

但是,组成联盟的首要条件就是交通条件。初始时,在这N个星球间有M条太空隧道。每条太空隧道连接两个星球,使得它们能够相互到达。若两个星球属于同一个联盟,则必须存在一条环形线路经过这两个星球,即两个星球间存在两条没有公共隧道的路径。

为了壮大联盟的队伍,这些星球将建设P条新的太空隧道。这P条新隧道将按顺序依次建成。一条新轨道建成后,可能会使一些星球属于同一个联盟。你的任务是计算出,在一条新隧道建设完毕后,判断这条新轨道连接的两个星球是否属于同一个联盟,如果属于同一个联盟就计算出这个联盟中有多少个星球。

输入
第1行三个整数N,M和P,分别表示总星球数,初始时太空隧道的数目和即将建设的轨道数目。

第2至第M+1行,每行两个整数,表示初始时的每条太空隧道连接的两个星球编号。

第M+2行至第M+P+1行,每行两个整数,表示新建的太空隧道连接的两个星球编号。这些太空隧道按照输入的顺序依次建成。

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

样例输入
3 2 1
1 2
1 3
2 3
样例输出
3
提示
【样例1说明】

新建成的隧道连接2、3两个星球。

这条隧道1和2,2和3,1和3之间都存在环形线路。1,2,3同属一个联盟,答案为3

【样例2】

alliance.in

alliance.out

5 3 4

1 2

4 3

4 5

2 3

1 3

4 5

2 4

No

3

2

5

【数据范围】

对于10%的数据有1≤N,M,P≤100;

对于40%的数据有1≤N,M,P≤2000;

对于100%的数据有1≤N,M,P≤200000。

我们考虑把原问题转换成树上加边的问题,如果加上这条边之前两个点不联通,那就必然不可能成为联盟,直接输出No。如果加边前为联通,那么我们可以采用并查集的方式维护联盟的个数(感觉写不清楚啊QaQ)。打个比方,在一棵树上给x,y两点之间加一条边,那么x到lca(x,y)与y到lca(x,y)之间这条路径就形成了一个环,然后我们根据深度维护一下并查集,就是把x,y都并到他们的祖先上就可以了(不懂就看一下代码吧)。

var
tot,i,n,m,p:longint;
f,g,c,u,v,dis,head,ret,next,h,d,pre:array[0..400044] of longint;
flag,fl:array[0..400044] of boolean;
procedure ins(u,v:longint);
begin
  tot:=tot+1;
  ret[tot]:=v;
  next[tot]:=head[u];
  head[u]:=tot;
end;

function find(i:longint):longint;
begin
  if f[i]<>i then f[i]:=find(f[i]);
  exit(f[i]);
end;

procedure build;
var
i,root1,root2:longint;
begin
  for i:=1 to n do
    f[i]:=i;
  for i:=1 to m do
  begin
    root1:=find(u[i]);
    root2:=find(v[i]);
    if root1<>root2 then
    begin
      ins(u[i],v[i]);
      ins(v[i],u[i]);
      fl[i]:=true;
      f[root1]:=root2;
    end;
  end;
  for i:=1 to p do
  begin
    root1:=find(g[i]);
    root2:=find(c[i]);
    if root1<>root2 then
    begin
      ins(g[i],c[i]);
      ins(c[i],g[i]);
      flag[i]:=true;
      f[root1]:=root2;
    end;
  end;
end;

procedure bfs;
var
i,u,v,st,ed,j:longint;
begin
  for i:=1 to n do
    if h[i]=0 then
    begin
      st:=0;
      ed:=1;
      d[1]:=i;
      h[i]:=1;
      pre[i]:=i;
      while st<ed do
      begin
        st:=st+1;
        u:=d[st];
        j:=head[u];
        while j<>0 do
        begin
          v:=ret[j];
          if h[v]=0 then
          begin
            ed:=ed+1;
            d[ed]:=v;
            pre[v]:=u;
            h[v]:=h[u]+1;
          end;
          j:=next[j];
        end;
      end;
    end;
end;

function ansesstor(u,v:longint):longint;
var
root1,root2,t:longint;
begin
  root1:=find(u);
  root2:=find(v);
  if root1=root2 then exit(root1);
  if h[root1]<h[root2] then begin t:=ansesstor(root1,pre[root2]); f[root2]:=t; dis[t]:=dis[t]+dis[root2]; end
                       else begin t:=ansesstor(pre[root1],root2); f[root1]:=t; dis[t]:=dis[t]+dis[root1]; end;
  exit(t);
end;

procedure solve;
var
i,fa:longint;
begin
  for i:=1 to n do
  begin
    f[i]:=i;
    dis[i]:=1;
  end;
  for i:=1 to m do
    if fl[i]=false then ansesstor(u[i],v[i]);
  for i:=1 to p do
  begin
    if flag[i] then begin writeln('No'); continue; end;
    fa:=ansesstor(g[i],c[i]);
    writeln(dis[fa]);
  end;
end;

begin
  readln(n,m,p);
  for i:=1 to m do
    readln(u[i],v[i]);
  for i:=1 to p do
    readln(g[i],c[i]);
  build;
  bfs;
  solve;
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值