jzoj 3453_连通块_并查集

发布于 2019-05-24  28 次阅读


题目描述

你应该知道无向图的连通块的数量,你应该知道如何求连通块的数量。当你兴奋与你的成就时,破坏王Alice拆掉了图中的边。当她发现,每删去一条边,你都会记下边的编号,同时告诉她当前连通块的个数。

然而,对边编号简直就是个悲剧,因为Alice为了刁难你,拆掉编号从l到r的边,当然你需要做的事情就是求连通块的个数。如果你答对了,Alice会把拆掉的边装好,迚行下一次破坏。如果你无法完成这个任务,Alice会彻底毁了你的图。

进行完足够多次之后,Alice觉得无聊,就玩去了,而你却需要继续做第三题。


思路

两个并查集,f[i]表示前i条边所连成的点的集合,g[j]表示后j条边所连成的点的集合。 那么对于一个询问 [l,r],我们就可以通过合并f[l-1]和g[r+1]两个集合来确定有几个联通块


#include 
#include 
using namespace std;
int n, m;
#define fill(x, y) memset(x, y, sizeof(x))
int f[10005][501], g[10005][501], a[10001][3], fl[501];
__attribute__((optimize("-O2")))
inline int find(int x, int t)
{
    //if (t > m || t < 1) return 0;
    if (!f[t][x]) return x;
    f[t][x] = find(f[t][x], t);
    return f[t][x];
}
__attribute__((optimize("-O2")))
inline int insert(int x, int y, int t)
{
    if (find(x, t) != find(y, t))
    {
        f[t][find(x, t)] = find(y, t);
        return 0;
    }
    return 0;
}
__attribute__((optimize("-O2")))
inline int find1(int x, int t)
{
    //if (t > m || t < 1) return 0;
    if (!g[t][x]) return x;
    g[t][x] = find1(g[t][x], t);
    return g[t][x];
}
__attribute__((optimize("-O2")))
inline int insert1(int x, int y, int t)
{
    //int xx = find1(x, t), yy = find1(y, t);
    if (find1(x, t) != find1(y, t))
    {
        g[t][find1(x, t)] = find1(y, t);
        return 0;
    }
    return 0;
}
__attribute__((optimize("-O2")))
inline int find2(int x)
{
    if (!fl[x]) return x;
    fl[x] = find2(fl[x]);
    return fl[x];
}
__attribute__((optimize("-O2")))
inline int insert2(int x, int y, int r)
{
    int xx = find2(x), yy = find2(y);
    if (xx != yy)
    {
        fl[xx] = yy;
        return 0;
    }
    return 0;
}
int main()
{
    freopen("connect.in","r",stdin);
    freopen("connect.out","w",stdout);
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        a[i][1] = x; a[i][2] = y;
        for (int j = 1; j <= n; j++)
        {
            f[i][j] = f[i - 1][j];
        }
        insert(x, y, i);
    }
    for (int i = m; i >= 1; i--)
    {
        int x = a[i][1], y = a[i][2];
        for (int j = 1; j <= n; j++)
        {
            g[i][j] = g[i + 1][j];
        }
        insert1(x, y, i);
    }
    int t;
    scanf("%d", &t);
    for (int i = 1; i <= t; i++)
    {
        int l, r;
        scanf("%d%d", &l, &r);
        int ans = 0;
        for (int j = 1; j <= n; j++)
            fl[j] = f[l - 1][j];
        for (int j = 1; j <= n; j++)
        {
            if (find2(j) != find1(j , r + 1))
            {
                insert2(find2(j), find1(j ,r + 1), r + 1);
            }   
        }
        for (int j = 1; j <= n; j++)
            if (fl[j] == 0) ans++;
        printf("%d\n", ans);
    }
}
]]>