JZOJ3453. 【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

分析

记录联通块的情况,我们一般会选择并查集。

我们先来考虑 L 的情况,

如果全部 L 都是 1 这样的做法是十分简单的,不过需要离线操作:

根据 R 的大小排一次序,然后就开始连边。

  • 但是现在的 L 不一定是1,所以我们要想一下其他方法。

题目要求删除 [l,r] 这个区间,所以我们需要连边的部分分成了两部分。

第一部分是1~l-1,第二部分是r+1~n

  • 那我们可不可以预处理出1~l-1 和 r+1~n的联通情况?

因为n很小,所以我们可以先预处理。

fi 表示连接1~i 的联通情况,是一个并查集。

同理,用 gi 表示连接i~n 的联通情况,也是一个并查集。

那么对于查询一个删除区间[l,r],我们就需要将 fl1 gr+1 合并起来就可以了。

合并两个并查集的时候,我们需要新建一个并查集,O(N)的时间来处理联通情况。

Code(c++)

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
using namespace std;

int n,t,k,l,r,ans,f1,f2,f3;
int f[10003][503],g[10003][503],m[503];
int x[10003],y[10003];

int get1(int y,int x)
{
    if(f[y][x]==x)return x;else f[y][x]=get1(y,f[y][x]);
    return f[y][x];
}

int get2(int y,int x)
{
    if(g[y][x]==x)return x;else g[y][x]=get2(y,g[y][x]);
    return g[y][x];
}

int get3(int x)
{
    if(m[x]==x)return x;else m[x]=get3(m[x]);
    return m[x];
}

int main()
{
    freopen("connect.in","r",stdin);
    freopen("connect.out","w",stdout);
    scanf("%d%d",&n,&t);

    for(int i=1;i<=n;i++)
    {
        f[0][i]=i;
        g[t+1][i]=i;
    }

    for(int i=1;i<=t;i++)
    {
        scanf("%d%d",&x[i],&y[i]);
        memcpy(f[i],f[i-1],sizeof(f[i]));
        f1=get1(i,x[i]);
        f2=get1(i,y[i]);
        f[i][f2]=f1;
    }

    for(int i=t;i;i--)
    {
        memcpy(g[i],g[i+1],sizeof(g[i]));
        f1=get2(i,x[i]);
        f2=get2(i,y[i]);
        g[i][f2]=f1;
    }

    scanf("%d",&k);
    for(int i=1;i<=k;i++)
    {
        scanf("%d%d",&l,&r);
        memcpy(m,f[l-1],sizeof(m)); 
        for(int j=1;j<=n;j++)
        {
            f1=get3(j);
            f2=get2(r+1,j);
            f3=get3(f2);
            if(f1!=f3)m[f3]=f1;
        }
        ans=0;
        for(int j=1;j<=n;j++)
            if(get3(m[j])==j)ans++;
        printf("%d\n",ans);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值