USACO 2020 OPEN Favorite Colors【並查集-啟發式合併-思考】

Starlight_Glimmer發表於2020-11-20

題目連結

題意簡述

仰慕喜歡同色奶牛的奶牛喜歡同色 (禁止套娃 ,求一種方案,奶牛喜歡的顏色種數最多,多種方案求字典序最小。

題目解析

這道題我最先想到的居然是二分+並查集,我在想啥

咳咳

首先,考慮一個比較簡單的情況,假如圖長這樣:

仰慕關係:\(6,4\)仰慕\(5\)\(3,1\)仰慕\(2\)

同一頭奶牛喜歡的顏色當然是相同的,\(6,4\)仰慕物件的喜好顏色一樣,所以\(6,4\)喜歡的顏色一樣,同理\(3,1\)喜歡的顏色一樣。我們把他們用並查集套起來,數有幾個塊就可以了

然後考慮更復雜的情況:

如圖,\(4\)是一隻花心的奶牛,它不僅仰慕\(5\),還仰慕\(2\)

同一頭奶牛喜歡的顏色當然是相同的,\(4\)只有一種喜歡的顏色,而\(6\)\(4\)喜歡顏色一樣,因為它們都喜歡\(5\),同理,\(3,1\)喜好顏色也和\(4\)一樣,那麼兩個連通塊就通過\(4\)聯通了。

為了方便寫程式碼,我們這樣看這個圖:(就是把邊反了個向,好寫程式碼

從兩隻站在仰慕鏈頂端的牛出發(其實也不一定是從它們出發,反正所有牛的兒子都要並在一起,話說也不一定有站在仰慕鏈頂端的牛,沒有保證是\(DAG\)),把它們的兒子並在一起,如果碰到了\(4\)這樣的花心結點,就把兩個並查集合在一起。

至於原圖,一個並查集裡的點可以當成一個點來處理,也就是要縮點。具體的方法很暴力,就是把別人的兒子接到我這裡來,然後把別人和它的兒子都從圖裡刪掉。為了保障複雜度,用啟發式合併,也就是小的集合合併到大集合上去。


►Code View

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
#define LL long long
#define N 200005
#define INF 0x3f3f3f3f
int rd()
{
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48); c=getchar();}
	return f*x;
}
vector<int>G[N];
int n,m,f[N]/*連通塊的大小 啟發式合併要用到 初始為-1 表示自己是根*/,c[N];
int Find(int x)
{
	if(f[x]<0) return x;
	return f[x]=Find(f[x]);
}
void dfs(int u)
{
    if(G[u].size()<2) return ;
    int x=Find(G[u][0]);
    for(int i=1;i<G[u].size();i++)
	{
        int y=Find(G[u][i]);
        if(x==y)continue;
        if(f[x]<=f[y])
		{
            f[x]+=f[y];
            f[y]=x;
            for(int j=0;j<G[y].size();j++)
                G[x].push_back(G[y][j]);
            G[y].clear();
        }
        else
		{
            f[y]+=f[x];
            f[x]=y;
            for(int j=0;j<G[x].size();j++)
                G[y].push_back(G[x][j]);
            G[x].clear();
            x=y;
        }
    }
    G[u].clear();
    G[u].push_back(x);
    dfs(x);
}
int main()
{
	memset(f,-1,sizeof(f));
	n=rd(),m=rd();
	for(int i=1;i<=m;i++)
	{
		int u=rd(),v=rd();
		G[u].push_back(v);
	}
	for(int i=1;i<=n;i++)
		dfs(i);
	int cnt=0;
	for(int i=1;i<=n;i++)
	{
		int fa=Find(i);
		if(!c[fa]) c[fa]=++cnt;
		printf("%d\n",c[fa]);
	}
	return 0;
}

相關文章