[Codeforces 920E]Connected Components?

Description

題庫連接c++

給你一個 \(n\) 個點 \(m\) 條邊的無向圖,求其補圖的連通塊個數及各個連通塊大小。算法

\(1\leq n,m\leq 200000\)spa

Solution

參考了 ww140142 的作法。題解也轉自該博客。.net

每次枚舉一個未處理過的點,而後從它開始寬搜出它所在的連通塊;code

具體是枚舉它的全部原圖的邊,標記起來,枚舉邊以後再枚舉全部的點,將未標記的點加入該連通塊,並加入隊列繼續寬搜;blog

爲了節約無用的枚舉,咱們還須要對全部點構建鏈表,將已經在某個塊內的點刪除;隊列

這個算法的複雜度是 \(O(n+m)\) 的;ip

緣由是每個點僅進行了一次寬搜的拓展;get

而且在每次拓展中,枚舉邊表總複雜度是 \(O(m)\)博客

而以後的枚舉剩下的點,咱們將點分爲兩部分:已標記的點的複雜度計在 \(O(m)\) 以內,而未標記的點將會被加入隊列,這個過程對每一個點也僅有一次。

綜上覆雜度爲 \(O(n+m)\)

Code

#include <bits/stdc++.h>
using namespace std;
const int N = 200000;

int n, m, u, v;
vector<int>to[N+5];
queue<int>Q;
int lst[N+5], nxt[N+5], ans[N+5], cnt;
int vis[N+5], undo[N+5];

void delet(int x) {nxt[lst[x]] = nxt[x], lst[nxt[x]] = lst[x]; }
void work() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++)
    scanf("%d%d", &u, &v), to[u].push_back(v), to[v].push_back(u);
    for (int i = 1; i < n; i++) nxt[i] = i+1, lst[i+1] = i;
    nxt[0] = 1;
    for (int i = 1; i <= n; i++)
    if (vis[i] == 0) {
        ans[++cnt] = 1;
        vis[i] = 1, Q.push(i); delet(i);
        while (!Q.empty()) {
        u = Q.front(); Q.pop();
        for (int j = 0, sz = to[u].size(); j < sz; j++)
            if (vis[to[u][j]] == 0) undo[to[u][j]] = 1;
        for (int j = nxt[0]; j; j = nxt[j])
            if (undo[j] == 0) {vis[j] = 1, ++ans[cnt]; delet(j); Q.push(j); }
            else undo[j] = 0;
        }
    }
    sort(ans+1, ans+cnt+1); printf("%d\n", cnt);
    for (int i = 1; i <= cnt; i++) printf("%d ", ans[i]);
}
int main() {work(); return 0; }
相關文章
相關標籤/搜索