"蔚来杯"2022牛客暑期多校训练营1 J Serval and Essay(启发式合并)
题意: 给定一张无环无重边图,初始所有点全为白色,你可以任意选择一个点使其变黑,其余白点可以被黑点污染的前提是 其所有入边全部为黑点 。 问该图最多黑点数为多少。
思路: 考虑合并集合,启发式合并。假如 1 1 1 号点染黑可以使得 2 2 2 号点变黑, 2 2 2 号点变黑可以使得 3 3 3 号点变黑。那么我们可以直接选择将 1 1 1 号点染色, 1 、 2 、 3 1、2、3 1、2、3 点处在一个集合之中。我们只需不断将两个集合合并即可(父亲集合和儿子集合),若儿子集合唯一入度为父亲集合的情况下。将 s i z e ( ) size() size() 小的集合合并到 s i z e ( ) size() size() 大的集合中去。总复杂度 O ( ( n + m ) l o g 2 n ) O((n+m)log^2n) O((n+m)log2n) 。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#pragma GCC optimize(3)
#define re register int
typedef pair<int,int>PII;
#define pb emplace_back
#define debug(a) cout<<a<<' ';
#define fer(i,a,b) for(re i=a;i<=b;i++)
#define der(i,a,b) for(re i=a;i>=b;i--)
int n;
const int N = 2e5+10;
set<int>in[N],out[N];
int p[N],si[N];
int cnt;
int find(int x){
if(p[x]!=x)p[x]=find(p[x]);
return p[x];
}
void merge(int u){
int fa=*in[u].begin();
u=find(u);
fa=find(fa);
if(u==fa)return ;
if(out[u].size()>out[fa].size()){
swap(u,fa);
}
si[fa]+=si[u];
p[u]=fa;
vector<int>vec;
for(auto x:out[u]){
out[fa].insert(x);
in[x].erase(u);
in[x].insert(fa);
if(in[x].size()==1)vec.push_back(x);
}
for(auto x:vec)merge(x);
}
void cf(){
cin>>n;
for(int i=1;i<=n;i++)p[i]=i,si[i]=1;
for(int i=1;i<=n;i++){
int m;
cin>>m;
for(int j=1;j<=m;j++){
int x;
cin>>x;
in[i].insert(x);
out[x].insert(i);
}
}
for(int i=1;i<=n;i++){
if(in[i].size()==1){
merge(i);
}
}
int ma=0;
for(int i=1;i<=n;i++){
int x=p[i];
ma=max(ma,si[x]);
}
for(int i=1;i<=n;i++){
in[i].clear();
out[i].clear();
}
cout<<"Case #"<<++cnt<<": "<<ma<<endl;
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int _=1;
cin>>_;
while(_--){
cf();
}
return 0;
}