CF-292D Connected Components 并查集 好题

D. Connected Components

题意

现在有n个点,m条编号为1-m的无向边,给出k个询问,每个询问给出区间[l,r],让输出删除标号为l-r的边后还有几个连通块?

思路

去除编号为[l,r]的边后,只剩下了[1,l-1]&&[r+1,m]两部分。

我们维护一个前缀以及后缀并查集,询问的时候把这两部分的边合并一下,就可以求出连通块的个数。

精辟!

代码

#include<bits/stdc++.h>
#include<vector>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<string>
#include<math.h>
#include<queue>
#include<map>
#define pb push_back
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=1e4+10;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;

int n,m,k;
struct dsu
{
    int fa[510],num;
    void init()
    {
        num=n;
        for(int i=1;i<=n;i++)
            fa[i]=i;
    }
    int find(int x)
    {
        if(fa[x]==x) return x;
        return fa[x]=find(fa[x]);
    }
    void join(int u,int v)
    {
        u=find(u);
        v=find(v);
        if(u==v) return;
        fa[u]=v;
        num--;
    }
}pre[N],suf[N];
struct note
{
    int u,v;
}arr[N];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%d%d",&arr[i].u,&arr[i].v);
    pre[0].init();
    for(int i=1;i<=m;i++)
    {
        pre[i]=pre[i-1];
        pre[i].join(arr[i].u,arr[i].v);
    }
    suf[m+1].init();
    for(int i=m;i>=0;i--)
    {
        suf[i]=suf[i+1];
        suf[i].join(arr[i].u,arr[i].v);
    }
    scanf("%d",&k);
    while(k--)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        dsu tmp=pre[l-1];
        for(int i=1;i<=n;i++)//把同一个点在两部分中的祖先合并
            tmp.join(tmp.find(i),suf[r+1].find(i));
        printf("%d\n",tmp.num);
    }
    return 0;
}
/*
*/
posted @ 2020-05-05 15:51  Valk3  阅读(125)  评论(0编辑  收藏  举报