CodeForces 292D Connected Components(并查集 前后缀)

博客详细介绍了如何利用并查集解决CodeForces 292D问题,该问题涉及在一系列边的删除与恢复后计算连通块数量。通过预处理并查集,博主提出了一种在限定时间内处理大量询问的方法,降低了空间复杂度,并提供了相应的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:给你n个点,m条边,现在q次询问,每次询问给你一个l, r, 问你删除第l至r条边后,连通块的数量。(每次询问

后又把所有边加上) n < 100, m < 1e4, q < 2e4


思路:想着暴力或是对询问区间进行排序再去操作,复杂度都很高,显然不行。每次询问是把一段区间的边删去,所

我们可以预处理出 1~i 和 j~m 条边构成的并查集,这样空间复杂度1e4*100,可以满足,每次询问,合并一下1~l-1

r+1~m这两个并查集,就能求得答案。


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 5e2+5;
const int maxm = 1e4+5;
int preL[maxm][maxn], preR[maxm][maxn], preTmp[maxn];
int u[maxm], v[maxm], n, m;

int Find(int *pre, int x)
{
    int r = x;
    while(pre[r] != r) r = pre[r];
    int i = x, j;
    while(i != r)
    {
        j = pre[i];
        pre[i] = r;
        i = j;
    }
    return r;
}

void join(int *pre, int x, int y)
{
    int a = Find(pre, x);
    int b = Find(pre, y);
    if(a != b)
        pre[b] = a;
}

void init()
{
    for(int i = 1; i <= n; i++)
            preL[0][i] = preR[0][i] = preL[m+1][i] = preR[m+1][i] = i;
    for(int i = 1; i <= m; i++)
    {
        for(int j = 1; j <= n; j++) preL[i][j] = preL[i-1][j];
        join(preL[i], u[i], v[i]);
    }
    for(int i = m; i >= 1; i--)
    {
        for(int j = 1; j <= n; j++) preR[i][j] = preR[i+1][j];
        join(preR[i], u[i], v[i]);
    }
}

int cal(int l, int r)
{
    for(int i = 1; i <= n; i++)
        preTmp[i] = preL[l-1][i];
    for(int i = 1; i <= n; i++)
        join(preTmp, preL[l-1][i], preR[r+1][i]);
    int ans = 0;
    for(int i = 1; i <= n; i++)
        if(i == Find(preTmp, i))
            ans++;
    return ans;
}

int main(void)
{
    while(cin >> n >> m)
    {
        for(int i = 1; i <= m; i++)
            scanf("%d%d", &u[i], &v[i]);
        init();
        int q;
        scanf("%d", &q);
        while(q--)
        {
            int l, r;
            scanf("%d%d", &l, &r);
            printf("%d\n", cal(l, r));
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值