Loading

题解 【CF920E Connected Components?】

\(\Large\texttt{CF920E}\)

\(\small\texttt{In my blog}\)

思路:并查集、暴力

思路和大家都差不多,但是想给一个细致一点的证明。

\(\large\texttt{Meaning}\)

给你一张 \(N\) 个点 \(M\) 条边的图,求这个图的补图的连通块个数以及每个连通块的大小。

\(\large\texttt{Solution}\)

在这个图中选一个度数最小的点 \(n\),和它连通(指补图)的点个数一定是所有点中最多的,将它们并在一起,然后对剩下没联通的点暴力操作,寻找连通块并合并。

虽然是暴力,但是它的复杂度也不是很大。

\(\large\texttt{Prove}\)

首先这样先选一个度最小的点先做联通操作肯定是其他方案中最优的,因为与它联通的点一定是最多的,省去了不必要的考虑。

令点数为 \(n\),边数为 \(m\)

先暴力枚举出那个度最小的点,它最坏情况最多\(\frac{m}{n}\) 条边连接 \(\frac{m}{n}\) 个点,那么剩下的\(n-\frac{m}{n}\) 个点是不用考虑的,因为都在一个连通块里面,考虑这 \(\frac{m}{n}\) 个点,而我们枚举这些点的出边,共 \(m-\frac{m}{n}\) 条,显然可过。

对每一个不与第一个联通的点,还要枚举一遍所有的点,复杂度为 \(\frac{m}{n}\times n = m\) 显然可过。

复杂度为 \(O(n+m)\)(最坏情况)。

\(\large\texttt{Code}\)

#include <bits/stdc++.h>
using namespace std;

#define PB push_back
const int N = 200000;
inline int read()
{
    register int s = 0;
    register bool neg = 0;
    register char c = getchar();
    for (; c < '0' || c > '9'; c = getchar())
        neg |= (c == '-');
    for (; c >= '0' && c <= '9'; s = s * 10 + (c ^ 48), c = getchar())
        ;
    return (neg ? -s : s);
}

int a, b, in[N + 10], fa[N + 10], scc, bel[N + 10], siz[N + 10];
bool vis[N + 10], vis1[N + 10];
vector<int> st[N + 10];

inline int f(int n)//并查集模板
{
    return fa[n] == n ? n : fa[n] = f(fa[n]);
}

signed main()
{
    a = read();
    b = read();
    int x, y;
    for (int i = 1; i <= b; i++)
    {
        x = read();
        y = read();
        st[x].PB(y);
        st[y].PB(x);
        in[x]++;
        in[y]++;
    }
    int mx = 1;
    for (int i = 1; i <= a; i++)
        fa[i] = i;
    for (int i = 2; i <= a; i++)
        if (in[i] < in[mx])//找到度最小的点
            mx = i;
    for (int i = 0; i < st[mx].size(); i++)
        vis[st[mx][i]] = 1;
    for (int i = 1; i <= a; i++)
        if (!vis[i])
            fa[i] = mx;
    for (int i = 1; i <= a; i++)
    {
        if (!vis[i] || i == mx)//对其他没有联通的点进行合并操作
            continue;
        memset(vis1, 0, sizeof(vis1));
        int f1 = f(i);
        for (int j = 0; j < st[i].size(); j++)
            vis1[st[i][j]] = 1;
        for (int j = 1; j <= a; j++)//暴力合并(((并非
            if (!vis1[j])
                fa[f(j)] = f1;
    }
    for (int i = 1; i <= a; i++)
        if (fa[i] == i)
            bel[i] = ++scc;
    for (int i = 1; i <= a; i++)
        siz[bel[f(i)]]++;
    sort(siz + 1, siz + scc + 1);
    printf("%d\n", scc);
    for (int i = 1; i <= scc; i++)
        printf("%d ", siz[i]);
    return 0;
}
posted @ 2020-08-11 07:37  RedreamMer  阅读(153)  评论(0编辑  收藏  举报