使用邻接表存图只有两个点AC

P1197 [JSOI2008] 星球大战

sienyu @ 2024-07-26 15:43:46

我没有用链式前向星建图,使用vector模拟临界矩阵,为什么只有2个点AC???```cpp

include<bits/stdc++.h>

define endl '\n'

using namespace std; //离线 逆向并查集

const int N = 2e5 + 10;

int root[N << 1]; // root数组

int n, m, k;

struct Edge { int x, y, i; // 当前边的起始终止节点 编号 } edge[N << 1]; bool isBroken[N << 1]; //标记当前节点有无被破坏 vector<int>broken; //保存被破坏的节点 vector<int>res; vector<int> graph[N << 1]; int findRoot(int x) { //寻找x的根节点 if (root[x] == x) return x; else return root[x] = findRoot(root[x]); } void merge(int x, int y) { //将x, y进行合并 x = findRoot(x); y = findRoot(y); if (x != y) { root[x] = y; // x 的根节点修改为 y } } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n >> m; for (int i = 0; i < n; i++) root[i] = i; // 并查集的初始化 for (int i = 1; i <= m; i++) { int x, y; cin >> x >> y; graph[x].push_back(y); graph[y].push_back(x); // 无向图 edge[i] = {x, y, i}; } cin >> k; for (int i = 1; i <= k; i++) { int t; cin >> t; isBroken[t] = 1; // t号节点被破坏 broken.push_back(t); //保存被破坏的节点离线处理 } // 任意两个节点之间没有联通 int total = n - k; //每建立一条边 则联通块的数量 - 1 所有节点被破坏后的联通块个数 for (int i = 1; i <= m; i++) { int x = edge[i].x; int y = edge[i].y; // x, y 均没有被破坏 且 x, y的根节点不同 int x_root = findRoot(x); int y_root = findRoot(y); if ((!isBroken[x] && !isBroken[y]) && (x_root != y_root)) { total--; merge(x, y); } } //此时total 为摧毁所有节点后的连通块 res.push_back(total); for (int i = broken.size() - 1; i >= 0; i--) { //将此时被摧毁的节点修复 int s = broken[i]; // 把s节点修复 看可以添加多少条边 int s_root = findRoot(s); isBroken[s] = 0; total++; for (int i = 0; i < graph[s].size(); i++) { int to = graph[s][i]; //所到达的点 if (!isBroken[to] && (s_root != findRoot(to))) { total--; //添加一条边则联通数 - 1; if (total <= 1) total = 1; merge(s, to); } } res.push_back(total); } //res 数组逆向打印 for (int i = res.size() - 1; i >= 0; i--) cout << res[i] << endl; return 0; }


by sienyu @ 2024-07-26 15:45:05

怎么我的代码成这样了???


by Obijeb @ 2024-07-26 15:57:25

@sienyu 大抵是底下的三个反引号挂掉了


by Obijeb @ 2024-07-26 16:03:13

#include<bits/stdc++.h>

#define endl '\n'

using namespace std; //离线 逆向并查集

const int N = 2e5 + 10;

int root[N << 1]; // root数组

int n, m, k;

struct Edge {
    int x, y, i; // 当前边的起始终止节点 编号 
}
edge[N << 1];
bool isBroken[N << 1]; //标记当前节点有无被破坏 
vector < int > broken; //保存被破坏的节点 
vector < int > res;
vector < int > graph[N << 1];
int findRoot(int x) { //寻找x的根节点 
    if (root[x] == x) return x;
    else return root[x] = findRoot(root[x]);
}
void merge(int x, int y) { //将x, y进行合并 
    x = findRoot(x);
    y = findRoot(y);
    if (x != y) {
        root[x] = y; // x 的根节点修改为 y 
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m;
    for (int i = 0; i < n; i++) root[i] = i; // 并查集的初始化 
    for (int i = 1; i <= m; i++) {
        int x, y;
        cin >> x >> y;
        graph[x].push_back(y);
        graph[y].push_back(x); // 无向图 
        edge[i] = {
            x,
            y,
            i
        };
    }
    cin >> k;
    for (int i = 1; i <= k; i++) {
        int t;
        cin >> t;
        isBroken[t] = 1; // t号节点被破坏 
        broken.push_back(t); //保存被破坏的节点离线处理 
    } // 任意两个节点之间没有联通 
    int total = n - k; //每建立一条边 则联通块的数量 - 1 所有节点被破坏后的联通块个数 
    for (int i = 1; i <= m; i++) {
        int x = edge[i].x;
        int y = edge[i].y; // x, y 均没有被破坏 且 x, y的根节点不同 
        int x_root = findRoot(x);
        int y_root = findRoot(y);
        if ((!isBroken[x] && !isBroken[y]) && (x_root != y_root)) {
            total--;
            merge(x, y);
        }
    } //此时total 为摧毁所有节点后的连通块 
    res.push_back(total);
    for (int i = broken.size() - 1; i >= 0; i--) { //将此时被摧毁的节点修复 
        int s = broken[i]; // 把s节点修复 看可以添加多少条边 
        int s_root = findRoot(s);
        isBroken[s] = 0;
        total++;
        for (int i = 0; i < graph[s].size(); i++) {
            int to = graph[s][i]; //所到达的点 
            if (!isBroken[to] && (s_root != findRoot(to))) {
                total--; //添加一条边则联通数 - 1; 
                if (total <= 1) total = 1;
                merge(s, to);
            }
        }
        res.push_back(total);
    } //res 数组逆向打印 
    for (int i = res.size() - 1; i >= 0; i--) cout << res[i] << endl;
    return 0;
}

by sienyu @ 2024-07-26 16:47:48

@Obijeb 这题恶心死我了


by Obijeb @ 2024-07-26 16:52:26

@sienyu 改好了

#include<bits/stdc++.h>

#define endl '\n'

using namespace std; //离线 逆向并查集

const int N = 2e5 + 10;

int root[N << 1]; // root数组

int n, m, k;

struct Edge {
    int x, y, i; // 当前边的起始终止节点 编号 
}
edge[N << 1];
bool isBroken[N << 1]; //标记当前节点有无被破坏 
vector < int > broken; //保存被破坏的节点 
vector < int > res;
vector < int > graph[N << 1];
int findRoot(int x) { //寻找x的根节点 
    if (root[x] == x) return x;
    else return root[x] = findRoot(root[x]);
}
void merge(int x, int y) { //将x, y进行合并 
    x = findRoot(x);
    y = findRoot(y);
    if (x != y) {
        root[x] = y; // x 的根节点修改为 y 
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m;
    for (int i = 0; i < n; i++) root[i] = i; // 并查集的初始化 
    for (int i = 1; i <= m; i++) {
        int x, y;
        cin >> x >> y;
        graph[x].push_back(y);
        graph[y].push_back(x); // 无向图 
        edge[i] = {
            x,
            y,
            i
        };
    }
    cin >> k;
    for (int i = 1; i <= k; i++) {
        int t;
        cin >> t;
        isBroken[t] = 1; // t号节点被破坏 
        broken.push_back(t); //保存被破坏的节点离线处理 
    } // 任意两个节点之间没有联通 
    int total = n - k; //每建立一条边 则联通块的数量 - 1 所有节点被破坏后的联通块个数 
    for (int i = 1; i <= m; i++) {
        int x = edge[i].x;
        int y = edge[i].y; // x, y 均没有被破坏 且 x, y的根节点不同 
        int x_root = findRoot(x);
        int y_root = findRoot(y);
        if ((!isBroken[x] && !isBroken[y]) && (x_root != y_root)) {
            total--;
            merge(x, y);
        }
    } //此时total 为摧毁所有节点后的连通块 
    res.push_back(total);
    for (int i = broken.size() - 1; i >= 0; i--) { //将此时被摧毁的节点修复 
        int s = broken[i]; // 把s节点修复 看可以添加多少条边 
        int s_root = findRoot(s);
        isBroken[s] = 0;
        total++;
        for (int i = 0; i < graph[s].size(); i++) {
            int to = graph[s][i]; //所到达的点 
            if (!isBroken[to] && (findRoot(s) != findRoot(to))) {
                total--; //添加一条边则联通数 - 1; 
                if (total <= 1) total = 1;
                merge(s, to);
            }
        }
        res.push_back(total);
    } //res 数组逆向打印 
    for (int i = res.size() - 1; i >= 0; i--) cout << res[i] << endl;
    return 0;
}

只改了合并前的判断


by sienyu @ 2024-07-26 18:08:30

@Obijeb 我去,大佬牛逼!!!我知道为啥了,下面有个merge, s的根是会变化的,所以每一次都要去查询


|