【NOIP2013中秋节模拟】连通块

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

The solution

Involved algorithm :并查集

specific

不妨
设一个F[i]数组,记录只添加前i条边时,所有节点的联通情况。
再设一个G[i]数组,记录只添加i-m条边时,所有节点的联通情况。
对于每一个询问只要把F[l-1],G[r+1]合并即可。

涉及并查集的合并,求联通块。

CODE

#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
#define fo(i,a,b) for (int i=a;i<=b;i++)
#define fd(i,a,b) for (int i=a;i>=b;i--)
#define N 505
#define M 10005
using namespace std;
int F[M][N],G[M][N],Merge[N],Mark[N],a[M][3];
int getF(int x,int i){ return F[i][x]==x?x:(F[i][x]=getF(F[i][x],i));}
int getG(int x,int i){ return G[i][x]==x?x:(G[i][x]=getG(G[i][x],i));}
int get(int x){ return Merge[x]==x?x:(Merge[x]=get(Merge[x]));}
int main()
{
    freopen("connect.in","r",stdin);
    freopen("connect.out","w",stdout);
    int n,m;
    scanf("%d%d",&n,&m);
    fo(i,1,m) scanf("%d%d",&a[i][1],&a[i][2]);
    fo(i,1,n) F[0][i]=i,G[m+1][i]=i;
    fo(i,1,m)
    {
        memcpy(F[i],F[i-1],sizeof(F[i]));
        int xx=getF(a[i][1],i);
        int yy=getF(a[i][2],i);
        if (xx!=yy) F[i][xx]=yy;    
    }
    fd(i,m,1)
    {
        memcpy(G[i],G[i+1],sizeof(G[i]));
        int xx=getG(a[i][1],i);
        int yy=getG(a[i][2],i);
        if (xx!=yy) G[i][xx]=yy;
    }   
    int k;
    scanf("%d",&k);
    while (k--)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        int ans=0;
        if (x>y) swap(x,y);
        memcpy(Merge,F[x-1],sizeof(Merge));
        fo(i,1,n) 
        {
            int xx=get(Merge[i]);
            int yy=get(G[y+1][i]);
            if (xx!=yy) Merge[xx]=yy;   
        }
        fo(i,1,n)
        {
            int xx=get(Merge[i]);
            if (Mark[xx]!=k) Mark[xx]=k,ans++;
        }
        printf("%d\n",ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值