Codeforces Round #346 (Div. 2) F. Polycarp and Hay(并查集+bfs)
发布日期:2021-05-08 15:17:29 浏览次数:13 分类:精选文章

本文共 3143 字,大约阅读时间需要 10 分钟。

问题是在判断是否可以将图中的边划分成k/x个连通块,每个连通块满足以下条件:

  • 每个连通块的大小不超过k/x。
  • 每个连通块内的边权值总和至少为z。
  • 以下是对问题的详细分析和解答:


    问题分析

    给定一个二维网格图,每个节点有边权值。问题要求判断是否存在一种连通块划分方式,将图中的边划分为k/x个连通块,每个连通块满足以下条件:

    • 连通块的大小不超过k/x。
    • 连通块内的边权值总和至少为z。

    这个问题可以通过并查集和广度优先搜索(BFS)来解决,具体步骤如下:


    解决思路

  • 排序节点:按照边权值从大到小排序节点,确保处理最大的边权值优先。

  • 并查集初始化:每个节点初始时是自己的父节点,数值初始化为1,表示每个节点初始时是一个连通块。

  • 处理每个节点:从大到小处理每个节点,检查其邻居。如果邻居满足条件,合并当前节点所在的连通块。

  • 检查条件:在处理每个节点时,检查是否满足k能被z整除,并计算t=k/z。如果当前连通块的数值满足条件,说明可以将图划分为t个连通块,每个连通块满足要求,返回“YES”。

  • 构造连通块:如果满足条件,调用BFS构造t个连通块,输出结果。


  • 解决代码

    #include 
    using namespace std;
    typedef long long ll;
    const int maxn = 1e6 + 5;
    const int dir[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
    int father[maxn], a[1001][1001];
    vector
    v;
    struct Node {
    int x, y, z;
    };
    bool cmp(const Node &a, const Node &b) {
    return a.z > b.z;
    }
    int findfather(int x) {
    if (x == father[x])
    return x;
    int i = findfather(father[x]);
    father[x] = i;
    return i;
    }
    void unit(int a, int b) {
    int fa = findfather(a), fb = findfather(b);
    if (fa != fb) {
    father[fa] = fb;
    num[fb] += num[fa];
    num[fa] = 0;
    }
    }
    void bfs(int x, int y, ll cnt, ll z) {
    queue
    q;
    q.push({x, y, a[x][y]});
    vis[x][y] = 1;
    cnt--;
    while (!q.empty()) {
    Node t = q.front();
    q.pop();
    int x = t.x, y = t.y, w = t.z;
    for (int j = 0; j < 4; ++j) {
    if (cnt == 0)
    break;
    int nx = x + dir[j][0], ny = y + dir[j][1];
    if (nx < 1 || nx > n || ny < 1 || ny > m || vis[nx][ny])
    continue;
    if (a[nx][ny] >= z) {
    vis[nx][ny] = 1;
    q.push({nx, ny, a[nx][ny]});
    cnt--;
    }
    }
    }
    for (int i = 1; i <= n; ++i)
    for (int j = 1; j <= m; ++j) {
    if (vis[i][j])
    printf("%d", z);
    else
    printf("0");
    if (j == m)
    printf("\n");
    }
    }
    int main() {
    ll k;
    for (int i = 0; i < maxn; ++i) {
    father[i] = i;
    num[i] = 1;
    }
    n, m, k;
    for (int i = 0; i < v.size(); ++i) {
    int x, y;
    scanf("%d", &a[i][0]);
    v.push_back({i + 1, i + 1, a[i][0]});
    }
    sort(v.begin(), v.end(), cmp);
    for (int i = 0; i < v.size(); ++i) {
    int x = v[i].x, y = v[i].y, z = v[i].z;
    for (int j = 0; j < 4; ++j) {
    int nx = x + dir[j][0], ny = y + dir[j][1];
    if (nx < 1 || nx > n || ny < 1 || ny > m)
    continue;
    if (a[nx][ny] >= z) {
    unit((nx - 1) * m + ny, (x - 1) * m + y);
    }
    }
    if (k % z != 0)
    continue;
    ll t = k / z;
    if (t <= num[findfather((x - 1) * m + y)]) {
    puts("YES");
    bfs(x, y, t, z);
    return 0;
    }
    }
    puts("NO");
    }

    代码解释

  • 并查集初始化:每个节点初始时是自己的父节点,数值为1。

  • 排序节点:按边权值从大到小排序,确保处理最大的边权值优先。

  • 处理节点:从大到小处理每个节点,检查邻居并进行并查集操作,合并连通块。

  • 条件检查:检查是否满足k能被z整除,并计算t=k/z。若当前连通块的数值满足条件,返回“YES”并构造连通块。

  • 构造连通块:调用BFS构造t个连通块,输出结果。


  • 结论

    通过以上方法,可以高效地判断图是否可以划分为k/x个连通块,每个连通块满足大小和边权值的条件。如果满足条件,返回“YES”;否则,返回“NO”。

    上一篇:Codeforces Round #325 (Div. 1) B. Phillip and Trains(bfs)
    下一篇:Codeforces Round #381 (Div. 1) B. Alyona and a tree (树上差分)

    发表评论

    最新留言

    路过,博主的博客真漂亮。。
    [***.116.15.85]2025年04月17日 04时01分29秒