洛谷 P1197 [JSOI2008]星球大战

题意简述

有n个点和m条通道,
现在按顺序破坏k个点
求每一次破坏后联通块的个数

题解思路

并查集,逆序做,
先假设给的k个星球全都被炸,求出此时的联通块个数,就是经过k次打击的联通块个数。
然后每次加上一个被炸的星球,就求出了经过每次次打击后的联通块个数

代码

#include <cstdio>
using namespace std;
int n, m, k, u, v, cnt, sum, x, y;
int d[401000], fa[401000], ans[401000];
int f[401000], to[801000], nxt[801000];
bool bd[401000];
void add_edge(int u, int v)
{
    to[++cnt] = v;
    nxt[cnt] = f[u];
    f[u] = cnt;
}
int gf(int x)
{
    return x == fa[x] ? x : fa[x] = gf(fa[x]);
}
int main()
{
    scanf("%d%d", &n, &m);
    for (register int i = 1; i <= m; ++i)
    {
        scanf("%d%d", &u, &v);
        add_edge(u, v);
        add_edge(v, u);
    }
    for (register int i = 0; i < n; ++i) fa[i] = i;
    scanf("%d", &k);
    for (register int i = 1; i <= k; ++i)
    {
        scanf("%d", &d[i]);
        bd[d[i]] = 1;
    }
    sum = n - k;
    for (register int i = 0; i < n; ++i)
        if (!bd[i])
            for (register int j = f[i]; j; j = nxt[j])
                if (!bd[to[j]])
                {
                    x = gf(i);
                    y = gf(to[j]);
                    if (x != y)
                    {
                        fa[y] = x;
                        --sum;
                    }
                }
    ans[k + 1] = sum;
    for (register int i = k; i; --i)
    {
        ++sum;
        bd[d[i]] = 0;
        x = gf(d[i]);
        for (register int j = f[d[i]]; j; j = nxt[j])
            if (!bd[to[j]])
            {
                y = gf(to[j]);
                if (x != y)
                {
                    fa[y] = x;
                    --sum;
                }
            }
        ans[i] = sum;
    }
    for (register int i = 1; i <= k + 1; ++i) printf("%d\n", ans[i]);
}

猜你喜欢

转载自www.cnblogs.com/xuyixuan/p/9594419.html