题解:P6279 [USACO20OPEN] Favorite Colors G
题目要求是,只要一个点有至少两个出点,就要把这两个出点合并。
并查集合并是好做的,但是染色似乎有些不好想。
因此考虑启发式合并,除了合并点,还要把边继承过去。
然后就是暴力模拟上述过程了,不必赘述。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
int siz[1000005];
int fa[1000005];
int col[1000005];
int vis[1000005];
int id;
vector<int>p[1000005];
vector<int>vec[1000005];
queue<int>q;
inline void merge(int x,int y){
if(p[x].size()<p[y].size()){
swap(x,y);
}
for (int i=0;i<vec[y].size();i++){
int v=vec[y][i];
vec[x].push_back(v);
}
for (int i=0;i<p[y].size();i++){
int v=p[y][i];
fa[v]=x;
p[x].push_back(v);
}
if (vec[x].size()>1){
q.push(x);
}
return;
}
signed main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int x,y;
cin>>x>>y;
vec[x].push_back(y);
}
for(int i=1;i<=n;i++){
fa[i]=i;
if(vec[i].size()>1){
q.push(i);
}
p[i].push_back(i);
}
while(q.size()){
int x=q.front();
q.pop();
while(vec[x].size()>1){
int a=fa[vec[x][0]];
int b=fa[vec[x][1]];
vec[x].erase(vec[x].begin());
if(a!=b){
merge(a,b);
}
}
}
for(int i=1;i<=n;i++){
if(!col[fa[i]]){
col[fa[i]]=++id;
}
cout<<col[fa[i]]<<'\n';
}
return 0;
}