題意簡述
仰慕喜歡同色奶牛的奶牛喜歡同色 (禁止套娃 ,求一種方案,奶牛喜歡的顏色種數最多,多種方案求字典序最小。
題目解析
這道題我最先想到的居然是二分+並查集,我在想啥
咳咳
首先,考慮一個比較簡單的情況,假如圖長這樣:
仰慕關係:\(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;
}