JZOJ 3453【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

Data Constraint

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

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

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

题解

  • 并查集。Collect!

  • 每一个 f[i] 为一个并查集,记录只添加前 i 条边时,所有节点的联通情况。

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

  • 对于每个询问只要把 f[l1] g[r+1] 合并至 H[] 中。

  • 最后在 H[] 中判断即可。

Code

#include<cstdio>
#include<cstring>
using namespace std;
int n,m;
struct data
{
    int x,y;
}a[10002];
int f[10002][501],g[10002][501],h[501];
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 getf(int k,int x)
{
    if(f[k][x]==x) return x;
    return f[k][x]=getf(k,f[k][x]);
}//F的查找
inline int getg(int k,int x)
{
    if(g[k][x]==x) return x;
    return g[k][x]=getg(k,g[k][x]);
}//G的查找
void init()
{
    n=read();
    m=read();
    for(int i=1;i<=m;i++) a[i].x=read(),a[i].y=read();
    for(int i=1;i<=n;i++) f[0][i]=g[m+1][i]=i;//初值
    for(int i=1;i<=m;i++)
    {
        memcpy(f[i],f[i-1],sizeof(f[i]));
        f[i][getf(i,a[i].x)]=getf(i,a[i].y);
    }//F的处理
    for(int i=m;i;i--)
    {
        memcpy(g[i],g[i+1],sizeof(g[i]));
        g[i][getg(i,a[i].x)]=getg(i,a[i].y);
    }//G的处理
}
inline int geth(int x)
{
    if(h[x]==x) return x;
    return h[x]=geth(h[x]);
}//H的查找
void work()
{
    int k=read();
    while(k--)
    {
        int l=read()-1,r=read()+1;
        memcpy(h,f[l],sizeof(h));
        for(int i=1;i<=n;i++) h[geth(h[i])]=geth(g[r][i]);//H的处理
        int ans=0;
        for(int i=1;i<=n;i++)
            if(h[i]==i) ans++;//H的判断
        printf("%d\n",ans);
    }
}
int main()
{
    freopen("connect.in","r",stdin);
    freopen("connect.out","w",stdout);
    init();
    work();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值