1. 程式人生 > >逆向思維 + 用並查集動態維護連通塊的個數——Luogu P1197題解

逆向思維 + 用並查集動態維護連通塊的個數——Luogu P1197題解

code space 題目 == 個數 sin getch 都是 delete

題目大意:

給你一個 $ n $ 個點的圖與 $ m $ 條邊,接下來給出一個長度為 $ k $ 個整數,按照給出整數的順序依次刪掉對應編號的點,求出一開始的連通塊的個數與接下來每次刪除一個點後的連通塊的個數。(連通塊就是一個點集,這個集合裏面的任意兩個點都可以互相到達)

思路:

大體思路:

這個題目如果正向考慮會很難做,因為我們要每次維護一個不斷刪點的圖的連通塊個數是很難弄的。所以我們要倒著考慮。假如我們把刪點的過程倒著考慮,就變成了從最後一個點往前添加進這個圖裏面的過程。所以這樣問題就變成了對一個圖每次添加進一個點和從這個點連出去的一些的邊,求出每一次添加後的圖的聯通塊的個數。

怎麽用並查集維護連通塊的個數:

我們首先先讓每個點都是獨立的,之後我們再按照給出的關系用並查集合並。如果兩個點可以合並,說明這兩個點之前再不同的連通塊裏,現在這兩個連通塊之間連通了,連通塊的個數就減一。如果這兩個點不用進行合並,說明這兩個點已經在一個連通塊裏了,連通塊的個數就保持不變。

代碼:

#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>

using namespace std;

const int N = 5e5 + 5;

int n, m, k;
int u, v;
int num = 0; //連通塊的個數
int arr[N];
int f[N]; //並查集
bool Is_Delete[N]; //標記那些點要刪除

vector <int> to[N];
vector <int> ans; //存儲答案
vector <int> point_rest; //被刪剩下的點

int get_f(int v) {
    if (f[v] == v) return v;
    else return f[v] = get_f(f[v]);
}

void merge(int u, int v) {
    int t1 = get_f(u);
    int t2 = get_f(v);
    if (t1 != t2) f[t2] = t1;
}

int read() {
    int s = 0, w = 1;
    char ch = getchar();
    while (ch < ‘0‘ || ch > ‘9‘) {
        if (ch == ‘-‘) w = -1;
        ch = getchar();
    }
    while (ch >= ‘0‘ && ch <= ‘9‘) {
        s = s * 10 + ch - ‘0‘;
        ch = getchar();
    }
    return s * w;
}

void write(int x) {
    if (x < 0) putchar(‘-‘), x = -x;
    if (x > 9) write(x / 10);
    putchar(x % 10 + ‘0‘);
}

int main() {
    n = read(), m = read();
    for (register int i = 1; i <= m; ++i) {
        u = read(), v = read();
        to[u].push_back(v);
        to[v].push_back(u);
    }
    k = read();
    for (register int i = 1; i <= k; ++i) {
        arr[i] = read();
        Is_Delete[arr[i]] = true; //標記要刪除的點
    }
    for (register int i = 0; i < n; ++i) {
        f[i] = i; //初始化並查集
        if (!Is_Delete[i]) {
            ++num;
            point_rest.push_back(i);
        }
    }
    vector <int> :: iterator it;
    for (it = point_rest.begin(); it != point_rest.end(); ++it) {
        vector <int> :: iterator it_tmp;
        for (it_tmp = to[*it].begin(); it_tmp != to[*it].end(); ++it_tmp) {
            if (!Is_Delete[*it_tmp]) {
                int t1 = get_f(*it);
                int t2 = get_f(*it_tmp);
                if (t1 != t2) {
                    merge((*it), (*it_tmp));
                    --num;
                }
            }
        }
    }
    for (register int i = k; i >= 1; --i) {
        ans.push_back(num);
        ++num; //添加進一個還沒被合並的點連通塊數量增加
        Is_Delete[arr[i]] = false;
        for (it = to[arr[i]].begin(); it != to[arr[i]].end(); ++it) {
            if (!Is_Delete[*it]) {
                int t1 = get_f(arr[i]);
                int t2 = get_f(*it);
                if (t1 != t2) {
                    merge(arr[i], (*it));
                    --num;
                }
            } 
        }
    }
    ans.push_back(num);
    for (register int i = (int)ans.size() - 1; i >= 0; --i) 
        write(ans[i]), putchar(‘\n‘);
    return 0;
}

逆向思維 + 用並查集動態維護連通塊的個數——Luogu P1197題解