UVa 564 Gaston

题目描述

Gaston\texttt{Gaston}Gaston是一家漫画杂志的员工,他最不喜欢的工作是归档邮件。他设计了一个用棍子和线构成的二叉搜索树系统来存放信件:每根棍子中间挂一封信,左端线下的所有信件编号小于棍子上的信,右端线下的所有信件编号大于棍子上的信。该系统形成一棵二叉搜索树,便于检索。

然而,该系统存在平衡问题。Gaston\texttt{Gaston}Gaston规定一根棍子平衡当且仅当其左侧信件数量等于右侧数量,或者左侧恰好比右侧多一封(即左子树大小等于右子树大小,或左子树大小等于右子树大小加一)。若右侧比左侧多,则视为不平衡。新增信件时,从根开始根据编号大小决定向左或向右,直至找到空线,然后挂上新棍子。随后,从根开始自上而下检查所有棍子的平衡情况,一旦发现不平衡的棍子,就执行一次“平衡步骤”,然后重新从根开始检查,直至整棵树完全平衡。

一次平衡步骤的定义为:对于不平衡的棍子sss,若右侧较重,则从右子树中取出最小信件lnewl_{\text{new}}lnew​替换sss上的旧信loldl_{\text{old}}lold​,并将loldl_{\text{old}}lold​插入到左子树的最大位置;若左侧过重(即左子树大小大于右子树大小加一),则从左子树中取出最大信件lnewl_{\text{new}}lnew​替换sss上的旧信loldl_{\text{old}}lold​,并将loldl_{\text{old}}lold​插入到右子树的最小位置。每次平衡步骤可能导致其他子树失衡,故需反复检查整个树。

题目要求:给定一棵初始已平衡的二叉搜索树,以及一系列待插入的新信件编号,计算在整个插入过程中总共需要执行多少次平衡步骤。

输入格式

第一行一个整数nnn,表示测试问题个数。

接下来nnn组数据,每组包含:

  • 一行(或跨多行)描述一棵二叉搜索树的先序表示,其中每个节点用一个正整数表示信件编号,空子树用000表示。数字之间至少一个空格分隔。
  • 下一行一个整数bbb,表示将要插入的信件数量。
  • 接下来bbb个整数(可能跨多行),表示依次插入的信件编号(1≤l≤10001 \le l \le 10001≤l≤1000)。

保证初始树是平衡的(即满足平衡条件),且所有信件编号互不相同。

输出格式

对于每组测试数据,输出一行一个整数,表示从开始插入第一封信到插入最后一封信为止,总共执行的平衡步骤数。

样例

输入

1 96 0 99 97 0 0 100 0 0 6 43 78 88 86 11 59

输出

11

题目分析

本题是一道动态维护平衡二叉搜索树的模拟题。核心操作包括:

  • 在二叉搜索树中插入一个新节点(按照编号大小递归找到空位)。
  • 判断任意节点的平衡状态:左子树大小与右子树大小是否满足sizeleft≥sizerightsize_{\text{left}} \ge size_{\text{right}}sizeleft​≥sizeright​且sizeleft≤sizeright+1size_{\text{left}} \le size_{\text{right}} + 1sizeleft​≤sizeright​+1,否则视为不平衡。
  • 对不平衡节点执行平衡步骤(规则固定)。
  • 每次插入后,需要反复扫描整棵树,直到所有节点均平衡,每次扫描按“根 → 左子树 → 右子树”的顺序检查,一旦执行一次平衡步骤,立即停止本次扫描,重新从根开始下一次扫描。

题目给出的初始树已经平衡,因此无需在插入前做任何调整。我们需要模拟整个插入过程,并累计平衡步骤数。

由于编号上限为100010001000,总节点数不超过100010001000,每次插入后最多可能触发O(O(O(节点数)))次平衡步骤,而平衡步骤本身涉及子树遍历(查找最值、删除、插入),因此总复杂度约为O(b⋅N⋅N)O(b \cdot N \cdot N)O(b⋅N⋅N),其中NNN为最终节点数,在100010001000量级下完全可行。

解题思路

数据结构设计

每个节点记录:

  • 信件编号val。
  • 左儿子指针left,右儿子指针right。

不维护子树大小字段,而是在需要判断平衡时,通过递归函数getSize实时计算子树大小。虽然这增加了常数开销,但避免了因平衡操作后更新size产生遗漏的风险,且节点数小,性能无碍。

核心操作实现

  1. 插入:按照二叉搜索树规则递归插入新节点。
  2. 计算子树大小:递归统计节点个数。
  3. 平衡判断:对某个节点,计算左右子树大小,若L < R或L > R + 1,则执行平衡步骤。
  4. 平衡步骤:
    • 若右子树较重(L < R):从右子树中取出最小节点(中序后继),将其值赋给当前节点;删除该最小节点;将当前节点的旧值插入到左子树的最大位置(即作为左子树的最右节点)。
    • 若左子树过重(L > R + 1):从左子树中取出最大节点(中序前驱),将其值赋给当前节点;删除该最大节点;将当前节点的旧值插入到右子树的最小位置(即作为右子树的最左节点)。
    • 每次操作后,无需更新任何size字段(因为我们实时计算)。
  5. 全局平衡检查:定义一个函数balanceCheck,从根开始按“根 → 左 → 右”的顺序遍历,一旦发现某个节点不平衡,立即调用balanceNode执行一次平衡步骤,并返回true;若整棵树都平衡,返回false。
  6. 主循环:对于每组数据,读入初始树(已平衡),然后依次读入每个待插入编号,执行insertBST,随后循环执行balanceCheck并累加次数,直到balanceCheck返回false。

正确性说明

  • 平衡步骤严格按照题目描述实现,保证了二叉搜索树的有序性不被破坏。
  • 每次插入后反复扫描整棵树,直到全局平衡,符合题目要求。
  • 实时计算子树大小,避免了维护size可能产生的错误,确保了平衡判断的准确性。

复杂度分析

设最终节点数为NNN(N≤1000N \le 1000N≤1000),每次插入后,平衡步骤次数最多为O(N)O(N)O(N)(因为每次平衡至少减少一个“逆序”对或类似势能),而每次平衡步骤涉及getSize(O(N)O(N)O(N))、查找最值(O(N)O(N)O(N))、删除(O(N)O(N)O(N))和插入(O(N)O(N)O(N)),因此单次平衡步骤复杂度为O(N)O(N)O(N)。总复杂度为O(b⋅N⋅N)O(b \cdot N \cdot N)O(b⋅N⋅N),在N≤1000N \le 1000N≤1000时足够快。

代码实现

// Gaston// UVa ID: 564// Verdict: Accepted// Submission Date: 2026-06-27// UVa Run Time: 0.000s//// 版权所有(C)2026,邱秋。metaphysis # yeah dot net#include<bits/stdc++.h>usingnamespacestd;structNode{intval;Node*left,*right;Node(intv):val(v),left(nullptr),right(nullptr){}};// 递归计算子树大小(不依赖存储字段,实时计算)intgetSize(Node*p){if(!p)return0;return1+getSize(p->left)+getSize(p->right);}// BST 插入(不更新 size)voidinsertBST(Node*&p,intv){if(!p){p=newNode(v);return;}if(v<p->val)insertBST(p->left,v);elseinsertBST(p->right,v);}// 寻找子树中的最小节点Node*findMin(Node*p){while(p&&p->left)p=p->left;returnp;}// 寻找子树中的最大节点Node*findMax(Node*p){while(p&&p->right)p=p->right;returnp;}// 删除子树中的最小节点(并释放内存)voiddeleteMin(Node*&p){if(!p)return;if(!p->left){Node*tmp=p;p=p->right;deletetmp;}else{deleteMin(p->left);}}// 删除子树中的最大节点voiddeleteMax(Node*&p){if(!p)return;if(!p->right){Node*tmp=p;p=p->left;deletetmp;}else{deleteMax(p->right);}}// 将值 v 插入到子树 p 中的最大位置(作为最右节点)voidinsertMax(Node*&p,intv){if(!p){p=newNode(v);return;}if(!p->right)p->right=newNode(v);elseinsertMax(p->right,v);}// 将值 v 插入到子树 p 中的最小位置(作为最左节点)voidinsertMin(Node*&p,intv){if(!p){p=newNode(v);return;}if(!p->left)p->left=newNode(v);elseinsertMin(p->left,v);}// 对单个节点执行一次平衡步骤,返回是否执行了操作boolbalanceNode(Node*&p){if(!p)returnfalse;intL=getSize(p->left);intR=getSize(p->right);// 右侧过重(左 < 右)if(L<R){Node*minNode=findMin(p->right);intoldVal=p->val;intnewVal=minNode->val;deleteMin(p->right);// 移除重侧的最小节点p->val=newVal;// 将其值赋给当前节点insertMax(p->left,oldVal);// 旧值插入轻侧(最大位置)returntrue;}// 左侧过重(左 > 右 + 1)elseif(L>R+1){Node*maxNode=findMax(p->left);intoldVal=p->val;intnewVal=maxNode->val;deleteMax(p->left);// 移除重侧的最大节点p->val=newVal;// 将其值赋给当前节点insertMin(p->right,oldVal);// 旧值插入轻侧(最小位置)returntrue;}returnfalse;}// 从当前节点开始,按根、左、右顺序检查并执行一次平衡,返回是否执行boolbalanceCheck(Node*&p){if(!p)returnfalse;if(balanceNode(p))returntrue;if(balanceCheck(p->left))returntrue;if(balanceCheck(p->right))returntrue;returnfalse;}// 读取树的先序表示(0 表示空)voidreadTree(Node*&p){intx;cin>>x;if(x==0){p=nullptr;return;}p=newNode(x);readTree(p->left);readTree(p->right);}intmain(){ios::sync_with_stdio(false);cin.tie(nullptr);intn;cin>>n;while(n--){Node*root=nullptr;readTree(root);intsteps=0;intb;cin>>b;for(inti=0;i<b;++i){intval;cin>>val;insertBST(root,val);// 插入后反复平衡直至整棵树满足条件while(balanceCheck(root))++steps;}cout<<steps<<'\n';// 此处可递归释放内存,但非必须}return0;}

总结

  • 模拟题的关键是严格按照题目描述的流程实现每一步,特别是平衡步骤中“从重侧取最值”以及“旧值插入轻侧最值位置”的细节,必须保证二叉搜索树的有序性不被破坏。
  • 平衡判断采用实时递归计算子树大小,简化了维护逻辑,虽然牺牲了一点性能,但在数据规模较小(N≤1000N \le 1000N≤1000)时完全可行,且降低了编码出错概率。
  • 全局扫描采用“根 → 左 → 右”的顺序,每次只执行一次平衡步骤后立即重新扫描,符合题目“重复从顶部检查直到全部平衡”的要求。
  • 初始树平衡:题目保证初始树已平衡,因此不需要在插入前进行平衡,否则会导致步骤计数错误(实际评测数据也证实了这一点)。
  • 内存管理:本程序未显式释放节点,但程序运行完即结束,对于竞赛题目可接受。若需严谨,可编写递归释放函数。

本题训练了对二叉树操作的熟练掌握,以及模拟复杂流程的能力。

相关新闻

MATLAB实现InSAR时序分析:从SLC到毫米级形变图的完整流程

MATLAB实现InSAR时序分析:从SLC到毫米级形变图的完整流程

简介&#xff1a;本资源是一套面向遥感科学与地质工程领域研究者的InSAR时序分析实战教程&#xff0c;聚焦MATLAB平台实现地表形变高精度反演&#xff0c;专为具备SAR基础与MATLAB编程能力的科研人员及工程师设计&#xff0c;助力地质灾害预警、城市沉降监测与冰川变化研究等实…

2026/10/20 13:13:05 阅读更多 →
OpenArm 7 自由度开源机械臂上手指南

OpenArm 7 自由度开源机械臂上手指南

OpenArm 7 自由度开源机械臂上手指南 【免费下载链接】openarm A fully open-source humanoid arm for physical AI research and deployment in contact-rich environments. 项目地址: https://gitcode.com/GitHub_Trending/op/openarm OpenArm 是一款开源的 7 自由度人…

2026/10/20 13:13:05 阅读更多 →
还在本地硬扛大模型?试试 Ollama Cloud,顺便把 OpenCode 也升级了

还在本地硬扛大模型?试试 Ollama Cloud,顺便把 OpenCode 也升级了

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 13:12:04 阅读更多 →

最新新闻

SecureCRT免安装版配置指南:离线串口调试与SSH便携部署

SecureCRT免安装版配置指南:离线串口调试与SSH便携部署

简介&#xff1a;本资源为SecureCRT绿色便携版免安装包&#xff0c;面向系统管理员、运维工程师及开发人员&#xff0c;解决多平台快速部署安全终端连接工具的需求&#xff0c;尤其适用于需在不同电脑间频繁切换、无管理员权限或追求轻量使用的场景。压缩包共137个文件&#xf…

2026/10/20 14:00:00 阅读更多 →
防火墙规则集时序配置:顺序、生效时间与安全空窗排查

防火墙规则集时序配置:顺序、生效时间与安全空窗排查

防火墙规则集里的时序配置&#xff0c;是我这几年排查安全事件时最常踩的隐形坑。规则明明写对了&#xff0c;但因为顺序放错、时间对象失效、批量下发存在中间态&#xff0c;导致规则的实际生效时间和预期完全对不上&#xff0c;轻则策略空转半个月没人发现&#xff0c;重则直…

2026/10/20 14:00:00 阅读更多 →
Python Pygame核心机制与实战:从零构建接苹果小游戏

Python Pygame核心机制与实战:从零构建接苹果小游戏

很多人在命令行里写过一阵Python脚本之后&#xff0c;总想搞点“能动的、看得见的东西”。Python Pygame模块就是你会碰到的第一块敲门砖&#xff1a;一个基于SDL封装、专门用来做2D图形和游戏的原生库。我第一次用它做窗口程序的时候&#xff0c;印象最深的不是那种“我能控制…

2026/10/20 14:00:00 阅读更多 →
100套可配置ECharts大数据大屏模板工程包

100套可配置ECharts大数据大屏模板工程包

简介&#xff1a;本资源是一套面向数据可视化工程师、前端开发人员及政企数字化项目实施者的专业级大屏模板集合&#xff0c;覆盖社区治理、智慧物业、数字政务、智能交通、金融银行等十余个高需求行业场景&#xff0c;有效解决业务数据实时监控与决策看板快速搭建难题。压缩包…

2026/10/20 14:00:00 阅读更多 →
AI加速剪辑工作流:字幕、粗剪与卡点自动化实战

AI加速剪辑工作流:字幕、粗剪与卡点自动化实战

我见过太多人剪视频&#xff0c;剪到怀疑人生。明明素材拍得挺好&#xff0c;脚本也没什么大问题&#xff0c;但就是一剪一整夜&#xff0c;导出前还在逐帧删吸气声、给字幕换行、把片尾黑场拖到精确位置。你以为是效率问题&#xff0c;其实是你把80%的时间花在了AI 10秒就能搞…

2026/10/20 14:00:00 阅读更多 →
深度学习训练调参实战:梯度、激活函数、学习率与优化器的链路

深度学习训练调参实战:梯度、激活函数、学习率与优化器的链路

入门深度学习时&#xff0c;很多人会被一连串名词劝退&#xff1a;激活函数、优化器、学习率、梯度。我第一次跑模型的时候也觉得它们是四个独立的调参项&#xff0c;今天调一下学习率&#xff0c;明天换一个激活函数&#xff0c;最后模型loss曲线像心电图一样乱跳&#xff0c;…

2026/10/20 13:59:00 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →