“蔚来杯“2022牛客暑期多校训练营1 J Serval and Essay(启发式合并)

"蔚来杯"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 123 点处在一个集合之中。我们只需不断将两个集合合并即可(父亲集合和儿子集合),若儿子集合唯一入度为父亲集合的情况下。将 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值