2022牛客多校J题 Serval and Essay

这篇博客探讨了一种图论问题,涉及无重边无自环的有向图。初始时选择一个点染黑,目标是最大化黑点数量。通过合并可以将黑点的父节点染色的策略,最终形成一个森林。文章介绍了如何找到能够合并的最大子树,并给出了C++代码实现来解决这个问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意
有一张 n 个点 m 条边的无重边无自环的有向图
初始时可以选择一个点染黑,其余点均为白点
若某个点所有入边的起点均为黑点,则该点可以被染黑
最大化图中黑点数量

如果一个点x可以将另一个点y染色,那么x的父节点也可以将y节点染色,既然这样,那么我们就可以将x和他的父亲节点进行合并。那么到最后一定会有一些不相交的集合构成一个森林,求森林中最大的那颗树即可。

/**
#include <algorithm>
#include <iostream>
#include <cmath>
#include <vector>
#include <stack>
#include <cstring>
#include <cstdio>
#include <queue>
#include <map>
#include <unordered_map>
#include <iomanip> //保留小数setprecision
#define endl '\n'
#define PI acos(-1.0) // 3.1415
#define Buff std::ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define mem(a, b) memset(a, b, sizeof(a))
#define lowbit(x) x & -x
#define ll long long
#define ull unsigned long long
#define eps 1e-10
#define int long long
//#pragma GCC optimize(2)
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 2e5 + 7, M = 5e5 + 7;
const int mod = 1e9 + 7;

int n, m, Time;
vector<int> q[N];
int fa[N], Size[N];
bool vis[N];

int find(int x)
{
    return fa[x] == x ? x : fa[x] = find(fa[x]);
}

void merge(int x, int y)
{
    Size[y] += Size[x], fa[x] = y, vis[x] = 1;
}

bool check(int x)//只有所有父节点都在一起并且不与自己在一起时才能合并。
{
    if (vis[x] || q[x].empty()) //如果该点遍历过或这个点没有父节点
        return false;
    int now = find(q[x][0]), t = q[x].size();
    for (int i = 1; i < t; i++) //判断是否所有父节点都在一个集合里(是否合并)
    {
        if (now != find(q[x][i]))
            return false;
    }
    if (now == x)//判断父节点是否与自己在一个集合(是否合并)
        return false;
    return true;
}

inline void solve()
{
    Time++;
    cin >> n;
    for (int i = 1; i <= n; i++)
        fa[i] = i, vis[i] = false, Size[i] = 1, q[i].clear();

    for (int i = 1; i <= n; i++)
    {
        int x, y;
        cin >> x;
        while (x--)
        {
            cin >> y;
            q[i].push_back(y);
        }
    }

    while (1)
    {
        int flag = 1;
        for (int i = 1; i <= n; i++)
        {
            if (check(i)) //判断这个点能否进行合并
            {
                flag = 0;
                merge(i, find(q[i][0]));
            }
        }
        if (flag) //如果所有点都不能进行合并,结束
            break;
    }
    int res = 0;
    for (int i = 1; i <= n; i++)
        res = max(res, Size[i]);
    cout << "Case #" << Time << ": " << res << endl;
    return;
}
signed main()
{
    int _ = 1;
    cin >> _;
    while (_--)
        solve();
    return 0;
}
/*
 *
 *  ┏┓   ┏┓+ +
 * ┏┛┻━━━┛┻┓ + +
 * ┃       ┃
 * ┃   ━   ┃ ++ + + +
 *  ████━████+
 *  ◥██◤ ◥██◤ +
 * ┃   ┻   ┃
 * ┃       ┃ + +
 * ┗━┓   ┏━┛
 *   ┃   ┃ + + + +Code is far away from bug with the animal protecting
 *   ┃   ┃ +                     神兽保佑,代码无bug 
 *   ┃    ┗━━━┓
 *   ┃        ┣┓
 *    ┃        ┏┛
 *     ┗┓┓┏━┳┓┏┛ + + + +
 *    ┃┫┫ ┃┫┫
 *    ┗┻┛ ┗┻┛+ + + +
 */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值