jzoj. 3453. 【NOIP2013中秋节模拟】连通块(connect)

Description

你应该知道无向图的连通块的数量,你应该知道如何求连通块的数量。当你兴奋与你的成就时,破坏王Alice拆掉了图中的边。当她发现,每删去一条边,你都会记下边的编号,同时告诉她当前连通块的个数。

然而,对边编号简直就是个悲剧,因为Alice为了刁难你,拆掉编号从l到r的边,当然你需要做的事情就是求连通块的个数。如果你答对了,Alice会把拆掉的边装好,迚行下一次破坏。如果你无法完成这个任务,Alice会彻底毁了你的图。

进行完足够多次之后,Alice觉得无聊,就玩去了,而你却需要继续做第三题。

Input

第一行两个整数n,m,表示点数和边数。

之后m行每行两个整数x,y,表示x与y之间有无向边。(按读入顺序给边编号,编号从1开始)

一行一个整数k,表示Alice的破坏次数。

之后k行,每行两个整数l,r。

Output

k行,每行一个整数。

Sample Input

6 5

1 2

5 4

2 3

3 1

3 6

6

1 3

2 5

1 5

5 5

2 4

3 3

Sample Output

4

5

6

3

4

2

Data Constraint

对于30%的数据,n<=100,k<=10

对于60%的数据,k<=1000

对于100%的数据,n<=500,m<=10000,k<=20000,1<=l<=r<=m

分析:显然就是求去掉一个区间后的连通块数,就是把其他的边并起来,求集合数。

我们发现每个答案,都是从前1……l-1到r+1……n这样的两个集合并起来,我们可以先预处理出
每一个 first[i] 为一个并查集,记录只添加前 i 条边时,所有节点的联通情况。

每一个 last[i] 也是一个并查集,记录只添加 i—m 条边时,所有节点的联通情况。

每次就是把first[l-1]和last[r+1]合并起来。

问题是,怎样把两个集合并起来呢?(考试时想了好久)

我们开一个数组h表示合并后的集合:

(1)把第一个集合的数据加入到h中。

(2)判断其中一个点i在两个集合中是否属于同一祖先,如果不属于,则可以把这两个祖先合并起来。

(3)得出最后的集合,判断集合数。

代码:

type
 arr=array [1..501] of longint;
var
 first,last:array [0..10001] of arr;
 g:arr;
 x,y:array [1..10001] of longint;
 n,k,i,j,l,r,x1,y1,ans,m,t:longint;

function find(x:longint;var p:arr):longint;
 var y,root,w:longint;
 begin
 y:=x;
   while p[y]>0 do
    y:=p[y];
   root:=y;
   y:=x;
   while p[y]>0 do
    begin
     w:=p[y];
     p[y]:=root;
     y:=w;
    end;
   find:=root;
 end;


procedure union(x,y:longint;var p:arr);
var s,t:longint;
 begin
  s:=find(x,p);
  t:=find(y,p);
  if s<>t then
    p[s]:=t;
 end;

begin
 assign(input,'connect.in');
 assign(output,'connect.out');
 reset(input);
 rewrite(output);
 readln(n,m);
 for i:=1 to m do
  readln(x[i],y[i]);
 for i:=1 to m do
  begin
   first[i]:=first[i-1];
   union(x[i],y[i],first[i]);
  end;
 for i:=m downto 1 do
  begin
   last[i]:=last[i+1];
   union(x[i],y[i],last[i]);
  end;
 readln(k);
 for i:=1 to k do
  begin
   readln(l,r);
   ans:=0;
   l:=l-1; r:=r+1;
   g:=first[l];
   for j:=1 to n do
   begin
    x1:=find(j,g);
    y1:=find(j,last[r]);
    if x1<>y1 then
      union(x1,y1,g);
   end;
   for j:=1 to n do
    if g[j]=0 then inc(ans);
   writeln(ans);
  end;
 close(input);
 close(output);
end.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值