Luogu6279 [USACO20OPEN]Favorite Colors G

Luogu6279 [USACO20OPEN]Favorite Colors G

并查集、启发式合并

这道题说实话挺阴间的。。。

首先,我们把对于一个节点的子节点合并(子节点数大于\(1\)的情况下才进行合并),对应的到达孙子节点的边也需要合并,如果是分层图的形式的话,这么做就直接做完了。

可是这道题奇葩的一点是有环,形成自己膜拜自己的情况(???)。

这样的情况手模一下,就是整颗子树会被合并入同一个集合内。

这里的代码妙不可言。

void dfs(int u)
{
    if (e[u].size()<2)
        return;
    int rt=getf(e[u][0]);
    for (int i=1;i<e[u].size();++i)
    {
        int v=getf(e[u][i]);
        if (v==rt)
            continue;
        f[v]=rt;
        if (e[rt].size()<e[v].size())
            swap(e[rt],e[v]);
        e[rt].insert(e[rt].end(),e[v].begin(),e[v].end());
    }
    e[u].clear();
    e[u].push_back(rt);
    dfs(rt);
}

我们每次与第一个子节点进行启发式合并。

当一个节点的儿子中含有自己时,如果\(rt \ne u\),就会扩张其他在子树内的点。

总有一个时刻\(rt=u\),这时注意我们的循环标准,也就是在合并过程中,\(e[u].size()\)会不断扩大,以致覆盖整棵子树,然后最终\(e[u].clear();e[u].push\_back(rt);\),子节点数降为\(1\),停止递归。

当然集合必须被合并到\(e[rt]\)尾部,因为\(rt=u\)时,我们需要遍历这些新的节点,而合并到尾部能够保证这些节点被遍历到。

这也是我们选择第一个子节点,而不是新建节点的理由,因为新建节点可能会导致\(e[u]\)整个集合被搬进新节点集合,从而导致死递归。

\(Code:\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#define N 200005
using namespace std;
int n,m,x,y,cnt;
int vis[N],f[N];
vector<int>e[N];
int getf(int x)
{
    return (f[x]==x)?x:(f[x]=getf(f[x]));
}
void dfs(int u)
{
    if (e[u].size()<2)
        return;
    int rt=getf(e[u][0]);
    for (int i=1;i<e[u].size();++i)
    {
        int v=getf(e[u][i]);
        if (v==rt)
            continue;
        f[v]=rt;
        if (e[rt].size()<e[v].size())
            swap(e[rt],e[v]);
        e[rt].insert(e[rt].end(),e[v].begin(),e[v].end());
    }
    e[u].clear();
    e[u].push_back(rt);
    dfs(rt);
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;++i)
    {
        scanf("%d%d",&x,&y);
        e[x].push_back(y);
    }
    for (int i=1;i<=n;++i)
        f[i]=i;
    for (int i=1;i<=n;++i)
        dfs(i);
    for (int i=1;i<=n;++i)
    {
        if (!vis[getf(i)])
            vis[getf(i)]=++cnt;
        printf("%d\n",vis[getf(i)]);
    }
    return 0;
}
posted @ 2021-02-05 15:52  GK0328  阅读(71)  评论(0编辑  收藏  举报