题解:洛谷 P1197 [JSOI2008] 星球大战

【题目来源】

洛谷:[P1197 JSOI2008] 星球大战 - 洛谷

【题目描述】

很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治着整个星系。

某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球。这些星球通过特殊的以太隧道互相直接或间接地连接。

但好景不长,很快帝国又重新造出了他的超级武器。凭借这超级武器的力量,帝国开始有计划地摧毁反抗军占领的星球。由于星球的不断被摧毁,两个星球之间的通讯通道也开始不可靠起来。

现在,反抗军首领交给你一个任务:给出原来两个星球之间的以太隧道连通情况以及帝国打击的星球顺序,以尽量快的速度求出每一次打击之后反抗军占据的星球的连通块的个数。(如果两个星球可以通过现存的以太通道直接或间接地连通,则这两个星球在同一个连通块中)。

【输入】

输入文件第一行包含两个整数 \(n,m\),分别表示星球的数目和以太隧道的数目。星球用 \(0 \sim n-1\) 的整数编号。

接下来的 \(m\) 行,每行包括两个整数 \(x,y\),表示星球 \(x\) 和星球 \(y\) 之间有“以太”隧道,可以直接通讯。

接下来的一行为一个整数 \(k\),表示将遭受攻击的星球的数目。

接下来的 \(k\) 行,每行有一个整数,按照顺序列出了帝国军的攻击目标。这 \(k\) 个数互不相同,且都在 \(0\)\(n-1\) 的范围内。

【输出】

第一行是开始时星球的连通块个数。接下来的 \(k\) 行,每行一个整数,表示经过该次打击后现存星球的连通块个数。

【输入样例】

8 13
0 1
1 6
6 5
5 0
0 6
1 2
2 3
3 4
4 5
7 1
7 2
7 6
3 6
5
1
6
3
5
7

【输出样例】

1
1
1
2
3
3

【算法标签】

《洛谷 P1197 星球大战》 #并查集# #连通块# #离线处理# #各省省选# #2008# #江苏#

【代码详解】

#include <bits/stdc++.h>
using namespace std;
const int N = 400005, M = 200005 * 2;

int h[N], from[M], to[M], ne[M], idx;
int n, m, k, tot;
int a[N], be[N], p[N], ans[N];

// 添加无向边
void add(int a, int b)
{
    from[idx] = a, to[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

// 并查集的查找操作
int find(int x)
{
    if (p[x] != x)
    {
        p[x] = find(p[x]);
    }
    return p[x];
}

int main()
{
    cin >> n >> m;
    memset(h, -1, sizeof(h));  // 初始化邻接表
    for (int i = 1; i <= m; i++)
    {
        int u, v;
        cin >> u >> v;
        add(u, v), add(v, u);  // 添加无向边
    }
    for (int i = 0; i < n; i++)
    {
        p[i] = i;  // 初始化并查集
    }
    cin >> k;
    for (int i = 1; i <= k; i++)
    {
        int x;
        cin >> x;
        a[i] = x;     // 记录要摧毁的城市
        be[x] = 1;    // 标记该城市为被摧毁状态
    }
    tot = n - k;  // 初始连通块数量

    // 初始化:只连接未被摧毁的城市
    for (int i = 1; i <= 2 * m; i++)
    {
        if (!be[from[i]] && !be[to[i]] && find(from[i]) != find(to[i]))
        {
            p[find(from[i])] = find(to[i]);  // 合并连通块
            tot--;
        }
    }
    ans[k + 1] = tot;  // 完全摧毁后的连通块数量

    // 逆序重建城市
    for (int j = k; j >= 1; j--)
    {
        int x = a[j];
        be[x] = 0;  // 恢复该城市
        tot++;      // 新增一个独立的连通块
        for (int i = h[x]; i != -1; i = ne[i])
        {
            if (!be[to[i]] && find(x) != find(to[i]))
            {
                p[find(x)] = find(to[i]);  // 合并连通块
                tot--;
            }
        }
        ans[j] = tot;  // 记录当前连通块数量
    }
    for (int i = 1; i <= k + 1; i++)
    {
        cout << ans[i] << endl;
    }
    return 0;
}

【运行结果】

8 13
0 1
1 6
6 5
5 0
0 6
1 2
2 3
3 4
4 5
7 1
7 2
7 6
3 6
5
1
6
3
5
7
1
1
1
2
3
3
posted @ 2026-03-23 09:50  团爸讲算法  阅读(2)  评论(0)    收藏  举报