[GESP202406 六级] 二叉树

news/2025/2/22 12:59:18/

题目描述

小杨有⼀棵包含 n n n 个节点的二叉树,且根节点的编号为 1 1 1。这棵二叉树任意⼀个节点要么是白色,要么是黑色。之后小杨会对这棵二叉树进行 q q q 次操作,每次小杨会选择⼀个节点,将以这个节点为根的子树内所有节点的颜色反转,即黑色变成白色,白色变成黑色。

小杨想知道 q q q 次操作全部完成之后每个节点的颜色。

输入格式

第⼀行一个正整数 n n n,表示二叉树的节点数量。

第二行 ( n − 1 ) (n-1) (n1) 个正整数,第 i i i 1 ≤ i ≤ n − 1 1\le i\le n-1 1in1)个数表示编号为 ( i + 1 ) (i+1) (i+1) 的节点的父亲节点编号,数据保证是⼀棵二叉树。

第三行一个长度为 n n n 01 \texttt{01} 01 串,从左到右第 i i i 1 ≤ i ≤ n 1\le i\le n 1in)位如果为 0 \texttt{0} 0,表示编号为 i i i 的节点颜色为白色,否则为黑色。

第四行⼀个正整数 q q q,表示操作次数。

接下来 q q q 行每行⼀个正整数 a i a_i ai 1 ≤ a i ≤ n 1\le a_i\le n 1ain),表示第 i i i 次操作选择的节点编号。

输出格式

输出一行一个长度为 n n n 01 \texttt{01} 01 串,表示 q q q 次操作全部完成之后每个节点的颜色。从左到右第 i i i 1 ≤ i ≤ n 1\le i\le n 1in) 位如果为 0 \texttt{0} 0,表示编号为 i i i 的节点颜色为白色,否则为黑色。

输入输出样例 #1

输入 #1

6
3 1 1 3 4
100101
3
1
3
2

输出 #1

010000

说明/提示

样例解释

第一次操作后,节点颜色为: 011010 \texttt{011010} 011010

第二次操作后,节点颜色为: 000000 \texttt{000000} 000000

第三次操作后,节点颜色为: 010000 \texttt{010000} 010000

数据范围

子任务编号得分 n n n q q q特殊条件
1 1 1 20 20 20 ≤ 1 0 5 \le 10^5 105 ≤ 1 0 5 \le 10^5 105对于所有 i ≥ 2 i\ge 2 i2,节点 i i i 的父亲节点编号为 i − 1 i-1 i1
2 2 2 40 40 40 ≤ 1000 \le 1000 1000 ≤ 1000 \le 1000 1000
3 3 3 40 40 40 ≤ 1 0 5 \le 10^5 105 ≤ 1 0 5 \le 10^5 105

对于全部数据,保证有 n , q ≤ 1 0 5 n,q\le 10^5 n,q105

提交链接

二叉树

思路分析

暴力搜索(40分)

读取输入

cin >> n; // 读取二叉树的结点个数
for(int i = 2; i <= n; i++) {cin >> f[i];  // 读取每个节点的父节点编号
}
cin >> color;
color = ' ' + color;  // 让 color[1] 对应编号 1(为了方便索引)
cin >> q;
  • n n n 是二叉树的节点数,编号从 1 1 1 n n n
  • f [ i ] f[i] f[i] 记录 编号为 i i i 的节点的父节点(即 f [ i ] f[i] f[i] i i i 的直接父节点)。
  • c o l o r color color 是一个 01 01 01 字符串,表示初始颜色。
  • q q q 是操作次数。

记录操作次数

while(q--) {  cin >> x;  // 选择节点 x 作为操作的根num[x] = (num[x] + 1) % 2;  // 记录节点 x 被操作的奇偶性
}

n u m [ x ] num[x] num[x] 记录以 x x x 为根的子树被操作的次数的奇偶性:

  • 如果 n u m [ x ] num[x] num[x] 是偶数,说明 x x x 的子树 颜色未变。
  • 如果 n u m [ x ] num[x] num[x] 是奇数,说明 x x x 的子树 颜色被翻转了一次。

遍历所有节点并计算最终颜色

for(int i = 1; i <= n; i++) {sum = 0;dfs(i);  // 计算 i 节点受到多少次翻转影响if(sum & 1) {  // 如果翻转次数是奇数if(color[i] == '1') color[i] = '0';else color[i] = '1';}
}

核心思想:

  • 遍历所有 i i i,计算 i i i 这个节点从 它自身到根节点 经过的 翻转次数 s u m sum sum
  • 如果 s u m sum sum 是奇数,就翻转 i i i 这个节点的颜色。

计算 i i i 受到的翻转次数

void dfs(int x) {if(num[x]) sum++;  // 如果 x 这个节点被操作了,就增加翻转次数if(f[x] == 0) return;  // 递归终止:已经到达根节点dfs(f[x]);  // 继续递归到父节点
}

递归 d f s ( x ) dfs(x) dfs(x)

  • 如果 x x x 本身是翻转点( n u m [ x ] = = 1 num[x] == 1 num[x]==1), s u m + + sum++ sum++
  • 继续向 f [ x ] f[x] f[x](父节点)递归,直到到达根节点。
  • 这样 s u m sum sum 记录的是 从 x x x 到根节点的所有翻转次数。

输出最终颜色

for(int i = 1; i <= n; i++)cout << color[i];

直接输出所有节点最终的颜色。

存在的问题

d f s ( i ) dfs(i) dfs(i) 重复计算了很多次。

  • 每个 i i i 都要从自己走到根,而 有些 i i i 的路径是重复的,造成大量冗余计算。
  • 树高最多是 O ( n ) O(n) O(n),最坏情况下变成 O ( n 2 ) O(n²) O(n2)。如果树是链状结构,则 d f s ( i ) dfs(i) dfs(i) 的调用深度最坏是 n n n,遍历所有 n n n 个节点就会导致 O ( n 2 ) O(n²) O(n2) 复杂度。

参考代码:

#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 1e5 + 9;int n , q , f[MAX_N] , x , num[MAX_N] , sum;
string color;void dfs(int x)
{if(num[x])sum++;if(f[x] == 0)return;dfs(f[x]);}int main()
{cin >> n; //二叉树的结点个数for(int i = 2; i <= n; i++)cin >> f[i];  //f[i]:结点i的父亲结点的编号cin >> color;color = ' ' + color;  //color[i]:编号为i的结点的颜色,color[i]=1为黑色 color[i]=0为白色cin >> q;while(q--)  //q次询问{cin >> x;  //选择编号为x的结点num[x] = (num[x] + 1) % 2;  //记录根结点为x的子树操作的次数}for(int i = 1; i <= n; i++){sum = 0;dfs(i);if(sum & 1){if(color[i] == '1')color[i] = '0';elsecolor[i] = '1';}}for(int i = 1; i <= n; i++)cout << color[i];return 0;
}

搜索优化(100分)

改进点:

  1. 改进翻转标记方式

    • 仍然使用 n u m [ x ] num[x] num[x] 记录操作次数,但不再逐个节点 D F S ( i ) DFS(i) DFS(i) 计算 s u m sum sum
    • 采用 子树 DFS 传播翻转状态,避免重复计算 s u m sum sum
  2. 高效的 DFS 处理方式

    • 构建子树关系:G[f[i]].push_back(i) 记录子节点,而不是用 f[x] 记录父节点。
    • 从根遍历一次 DFS 传递翻转状态:
      • c h e c k check check 变量标记当前节点是否应被翻转。
      • 遍历整个子树,每个节点仅访问一次,避免重复计算。
  3. 时间复杂度分析

    • 树的遍历是 O ( n ) O(n) O(n)
    • 建树 O ( n ) O(n) O(n)
    • 一次 DFS 处理整个树 O ( n ) O(n) O(n)
    • 总时间复杂度 O ( n ) O(n) O(n)

参考代码:

#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 1e5 + 9;int n , q , f[MAX_N] , x , num[MAX_N] , sum;
string color;
vector<int>G[MAX_N];void dfs(int x)
{if(num[x])sum++;if(f[x] == 0)return;dfs(f[x]);
}void dfs(int x , bool check)
{if(check && num[x])   //check为父结点带来的影响  num[x]为本身是否需要操作check = false;else if(!check && num[x] || check && !num[x])check = true;if(check){if(color[x] == '1')color[x] = '0';elsecolor[x] = '1';}for(auto it : G[x])dfs(it , check);
}int main()
{cin >> n; //二叉树的结点个数for(int i = 2; i <= n; i++){cin >> f[i];  //f[i]:结点i的父亲结点的编号G[f[i]].push_back(i);  //儿子存储法}cin >> color;color = ' ' + color;  //color[i]:编号为i的结点的颜色,color[i]=1为黑色 color[i]=0为白色cin >> q;while(q--)  //q次询问{cin >> x;  //选择编号为x的结点num[x] = (num[x] + 1) % 2;  //记录根结点为x的子树操作的次数}dfs(1 , false);for(int i = 1; i <= n; i++)cout << color[i];return 0;
}

http://www.ppmy.cn/news/1574161.html

相关文章

C#功能测试

一、List 内部元素为引用 src[0]的Name为"11"&#xff0c;说明修改了引用 List<Source> src new List<Source>(); src.Add(new Source() { Name "1", Age 1, Description "1" }); src.Add(new Source() { Name "2"…

一篇文章理解常用的前端设计模式

前端设计模式 一.设计模式概览 设计模式是针对软件设计开发过程中反复出现的某类问题的通用解决方案。设计模式更多的是指导思想和方法论&#xff0c;而不是现成的代码&#xff0c;每种设计模式都有每种语言中的具体实现方式。学习设计模式更多是理解各个模式的内在思想和解决…

深入了解 mica-auto:自动生成 Java SPI 和 Spring Boot 配置的利器

1. mica-auto 出现的背景 在 Java 开发中,尤其是在构建 Spring Boot 项目和使用 Java SPI(Service Provider Interface)机制时,开发者常常面临配置文件编写的繁琐问题。 1.1 Java SPI 的配置痛点 Java SPI 是一种服务发现机制,允许第三方为程序提供扩展实现。使用 SPI …

基于LangGraph和Ollama实现可调用AI搜索引擎Tavily的Agentic RAG问答机器人

这篇博客将和大家分享如何快速实现一个运行逻辑相较于传统链式RAG&#xff08;用户询问 -> 检索相关信息作为上下文 -> LLM推理回复&#xff09;更为智能、适应性更强的Agentic RAG Chatbot&#xff08;实现思路参考 Langgraph Agentic RAG 实现官方文档教程&#xff09;…

UE_C++ —— Container TSet

目录 一&#xff0c;TSet 二&#xff0c;Creating and Filling a Set Editing UPROPERTY TSets 三&#xff0c;Iteration 四&#xff0c;Queries 五&#xff0c;Removal 六&#xff0c;Sorting 七&#xff0c;Operators 八&#xff0c;Slack 九&#xff0c;DefaultKe…

游戏引擎学习第116天

回顾昨天的工作 本次工作内容主要集中在游戏开发的低级编程优化&#xff0c;尤其是手动优化软件渲染。工作目的之一是鼓励开发者避免依赖外部库&#xff0c;而是深入理解代码并进行优化。当前阶段正进行SIMD&#xff08;单指令多数据&#xff09;优化&#xff0c;使用Intel推荐…

Innovus中快速获取timing path逻辑深度的golden脚本

在实际项目中我们经常会遇到一条timing path级数特别多&#xff0c;可能是一两页都翻不完。此时&#xff0c;我们大都需要手工去数这条path上到底有哪些是设计本身的逻辑&#xff0c;哪些是PR工具插入的buffer和inverter。 数字IC后端手把手培训教程 | Clock Gating相关clock …

HTTP SSE 实现

参考&#xff1a; SSE协议 SSE技术详解&#xff1a;使用 HTTP 做服务端数据推送应用的技术 一句概扩 SSE可理解为&#xff1a;服务端和客户端建立连接之后双方均保持连接&#xff0c;但仅支持服务端向客户端推送数据。推送完毕之后关闭连接&#xff0c;无状态行。 下面是基于…