• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 众包
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

RomanLin

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

【无根树建图】洛谷 P10723 [GESP202406 七级] 黑白翻转

题目

https://www.luogu.com.cn/problem/P10723

题解

这题有两种解法,可以自底向上进行求解,也可以自顶向下进行求解。

解法一:自底向上 无根树建图

异或建图又称为无根树建图,指的是一颗无根树用一维数组 \(pa[i]\) 存储节点 \(i\) 的相邻节点信息和一维数组 \(d[i]\) 存储节点 \(i\) 的相邻节点个数。由于异或运算具有自反性,且树必定不存在环,因此可以用 \(d[i] \leq 1\) 为判定条件做到从叶子节点转移到其父节点。建图时使用 \(pa\) 一层一层异或压缩相邻节点信息,遍历的时候一层一层的解码。

异或建图的优点是不需要使用常规的二维数组(邻接表)存放图的信息,而只需要使用两个一维数组即可,运行速度比邻接表的方式约快一倍。如果需要效率更高的话,可以考虑链式前向星的方式存储图的信息,速度会更优秀。

解法二:自顶向下 转化为有根树

以任意一个黑色节点作为根节点,从该节点出发以后序遍历的思路做一趟 dfs,用一个二元组维护从子节点到当前节点的白色节点总个数以及是否存在黑色节点的信息,最后答案就是根节点的二元组的白色节点总个数。

参考代码

解法一:自底向上 无根树建图(异或建图)

#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
using namespace std;

constexpr int N = 1e5 + 7;
constexpr int INF = 0x3f3f3f3f;
int n, m, w, ans;
int a[N];
int d[N] = {INF};//维护相邻节点个数
int pa[N];//维护相邻节点信息

int main() {
    IOS
    cin >> n;
    for (int i = 1; i <= n; ++ i) cin >> a[i];
    ans = n - reduce(a + 1, a + 1 + n, 0);
    for (int i = 1; i < n; ++ i) {
        cin >> m >> w;
        d[m] ++, d[w] ++;
        //异或建图(无根树建图)
        pa[m] ^= w, pa[w] ^= m;
    }
    for (int i = 1; i <= n; ++ i) {
        for (int j = i, k; d[j] <= 1 && !a[j]; j = k) {
            -- ans;
            d[j] = INF;
            //由于节点 j 是叶子节点,因此 pa[j] 必定是叶子节点的父节点
            k = pa[j];
            //异或具有自反性,因此让叶子节点 j 的父节点 pa[j] 的相邻节点信息 pa[pa[j]] 对 j 取异或,即可去掉叶子节点 j
            pa[k] ^= j;
            //叶子节点 j 的父节点 pa[j] 由于去掉了一个相邻节点,因此度数减少 1
            d[k] --;
        }
    }
    cout << ans << '\n';
    return 0;
}

解法二:自顶向下 转化为有根树(从一个黑节点出发)

#include<bits/stdc++.h>
#define eb(x) emplace_back(x)
using namespace std;

constexpr int N = 1e5 + 7;
int n, m, w;
char a[N];
vector<int> g[N];//邻接表
struct Node {
    int sum;//统计白节点个数//统计白节点个数
    bool ex;//是否存在黑节点
};

Node dfs(int root, int fa) {
    bool ex = a[root] == '1';//是否存在黑节点
    int sum = !ex;//统计白节点个数
    for (auto &son: g[root]) {
        if (fa != son) {
            Node node = dfs(son, root);
            if (node.ex) {//子节点存在黑节点
                ex = true;
                sum += node.sum;
            }
        }
    }
    return {sum, ex};
}

int main() {
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    cin >> n;
    int root = -1;
    for (int i = 1; i <= n; ++ i) {
        cin >> a[i];
        if (a[i] & 1) root = i;//找到一个黑节点作为根节点
    }
    if (root == -1) {//没有黑节点,说明无需删除,直接返回即可
        cout << 0 << '\n';
        return 0;
    }
    for (int i = 1; i < n; ++ i) {
        cin >> m >> w;
        g[m].eb(w), g[w].eb(m);
    }
    cout << dfs(root, -1).sum << '\n';
    return 0;
}

posted on 2026-01-09 00:14  RomanLin  阅读(8)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3