Codeforces 292D [Connected Components]--并查集的巧妙应用

【链接】
Codeforces 292D

【题目大意】
给你N个节点和M条边以及Q次询问。对于每一次询问给你两个数li,ri表示不取区间li~ri的边,并输出连通块的个数。

【解题报告】
因为li~ri里的边是不取的,求的又是连通块的个数,所以考虑要取的边。首先想到的是一个O(N*M*Q)的暴力直接搞,但是时间复杂度扛不住。这是就想到了预处理记l[i]表示1~i的边构成的图中每个节点的父亲是谁,r[i]表示i~n的边构成的图中每个节点的父亲是谁,预处理的时间复杂度为O(N*M)。所以对于每次询问就可以O(N)回答,时间复杂度O(N*Q)。

#include<cstdio>
using namespace std;
const int maxn=505,maxm=10005;
int n,m,Q;
struct wjd
{
    int x,y;
}a[maxm];
struct UF
{
    int fa[maxn];
    void Clear(){for (int i=1; i<=n; i++) fa[i]=i;}
    int Getfa(int x){if (fa[x]==x) return x; fa[x]=Getfa(fa[x]); return fa[x];}
    void Merge(int x,int y) {x=Getfa(x); y=Getfa(y); if (x!=y) fa[x]=y;}
}L[maxm],R[maxm],Fa;//并查集
inline int Read()
{
    int res=0;
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') res=res*10+ch-48,ch=getchar();
    return res;
}
int Query(int l,int r)
{
    for (int i=1; i<=n; i++) Fa.fa[i]=L[l].fa[i];
    for (int i=1; i<=n; i++) Fa.Merge(i,R[r].Getfa(R[r].fa[i]));
    int sum=0;
    for (int i=1; i<=n; i++)
     if (i==Fa.Getfa(i)) sum++; 
    return sum;
}
int main()
{#include<cstdio>
using namespace std;
const int maxn=505,maxm=10005;
int n,m,Q;
struct wjd
{
    int x,y;
}a[maxm];
struct UF
{
    int fa[maxn];
    void Clear(){for (int i=1; i<=n; i++) fa[i]=i;}
    int Getfa(int x){if (fa[x]==x) return x; fa[x]=Getfa(fa[x]); return fa[x];}
    void Merge(int x,int y) {x=Getfa(x); y=Getfa(y); if (x!=y) fa[x]=y;}
}L[maxm],R[maxm],Fa;
inline int Read()
{
    int res=0;
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') res=res*10+ch-48,ch=getchar();
    return res;
}
int Query(int l,int r)
{
    for (int i=1; i<=n; i++) Fa.fa[i]=L[l].fa[i];
    for (int i=1; i<=n; i++) Fa.Merge(i,R[r].Getfa(R[r].fa[i]));
    int sum=0;
    for (int i=1; i<=n; i++)
     if (i==Fa.Getfa(i)) sum++; 
    return sum;
}
int main()
{
    freopen("292D.in","r",stdin);
    freopen("292D.out","w",stdout);
    n=Read(); m=Read();
    for (int i=1; i<=m; i++) a[i]=(wjd){Read(),Read()};
    L[0].Clear(); R[m+1].Clear();
    for (int i=1; i<=m; i++) L[i]=L[i-1],L[i].Merge(a[i].x,a[i].y);
    for (int i=m; i; i--) R[i]=R[i+1],R[i].Merge(a[i].x,a[i].y);
    Q=Read();
    for (int i=1; i<=Q; i++)
    {
        int l=Read(),r=Read();
        printf("%d\n",Query(l-1,r+1));
    }
    return 0;
}
    n=Read(); m=Read();
    for (int i=1; i<=m; i++) a[i]=(wjd){Read(),Read()};
    L[0].Clear(); R[m+1].Clear();
    for (int i=1; i<=m; i++) L[i]=L[i-1],L[i].Merge(a[i].x,a[i].y);//构造L数组
    for (int i=m; i; i--) R[i]=R[i+1],R[i].Merge(a[i].x,a[i].y);//构造R数组
    Q=Read();
    for (int i=1; i<=Q; i++)
    {
        int l=Read(),r=Read();
        printf("%d\n",Query(l-1,r+1));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值