洛谷 P9754 [CSP-S 2023] 结构体 题解

题目传送门
洛谷博客 个人博客站

CSP-S 2023 T3 结构体 题解

基本思路

本题主要考查编码能力,所以直接给出基本思路:

  • 由于可以递归式的创建元素,最多可以同时存在 10 0 100 100^{100} 100100 个不同的基础类型的元素。即使算上最大地址的限制,元素的数量也能达到 1 0 18 10^{18} 1018。显然,依次构造每个元素,在空间和时间上都是无法接受的。
  • 然而,由于询问数量有限,真正能在查询时用到的元素数量相对很少。因此,我们只需维护一个顶层元素(不隶属于任何其他元素的元素)列表,再根据查询的地址或名称逐层向下找到需要的元素即可。以下是四种操作的具体做法:
    • 对于 o p = 1 op=1 op=1:储存当前类型信息,计算大小和对齐要求并输出。
    • 对于 o p = 2 op=2 op=2:用一个变量记录当前第一个可分配内存的地址,操作时先对齐后计算、输出。
    • 对于 o p = 3 op=3 op=3:从顶层开始,逐层向下寻找,计算地址并输出。
    • 对于 o p = 4 op=4 op=4:从顶层开始,维护当前考查的元素地址,并与给定地址比对,最终输出底层元素名称。

由以上思路,很容易想到下面三种类型的存储方式:

  1. 类型名称作为类型的唯一的标识符。这是最直观的做法,但是效率低下且使用起来较为繁琐,pass。
  2. 用 map 将类型名称映射到序号,来代表一种数据类型。相比第一种做法,效率高了很多,但是写起来仍然很麻烦,pass。
  3. 用结构体存储类型信息,并使用指针来处理类型之间的关联。这种做法不仅高效,而且编码时也很直观,所以我们将采用这种存储方式。

分步详解

准备

LL 表示 long longsetmax(x, y) 等同于 x = max(x, y)

inline void setmax(int& x, int y)
{if(x < y) x = y;
}using LL = long long;
数据类型的存储

定义 struct DataType,表示一种数据类型:

struct DataType
{const string name; // 类型名LL size, actual_size; // 对齐后的大小和实际大小(有数据的部分的长度)int indent; // 对齐要求vector<pair<DataType*, string>> members; // 类型成员,<成员类型指针,成员名称> 方式存储
};

对齐操作,依照如下公式:
对齐后的地址 = ⌈ 对齐前的地址 对齐要求 ⌉ × 对齐要求 {对齐后的地址} = \lceil \frac {对齐前的地址} {对齐要求} \rceil \times {对齐要求} 对齐后的地址=对齐要求对齐前的地址×对齐要求

inline LL shift(LL addr)
{return addr % indent? (addr / indent + 1) * indent: addr;
}

维护操作,用于操作 1 1 1 后计算大小:

inline void maintain()
{size = indent = 0;for(const auto& m: members){setmax(indent, m.first->indent);size = m.first->shift(size) + m.first->size;}actual_size = size;size = shift(size);
}

注意 shiftmaintain 都是 DataType 的成员函数。

主函数中,用一个 unordered_map 记录类型名到数据类型的映射关系

unordered_map<string, DataType*> types;

添加基本类型

auto add_base_type = [&](string name, int size) -> void {DataType* t = new DataType(name);t->size = t->indent = t->actual_size = size;types[name] = t;
};
add_base_type("byte", 1);
add_base_type("short", 2);
add_base_type("int", 4);
add_base_type("long", 8);
操作 1:定义类型

由于 DataType 中已经实现维护操作,简单处理一下输入即可:

string s;
int k;
cin >> s >> k;
DataType* type = new DataType(s);
types[s] = type;
type->members.resize(k);
for(auto& m: type->members)
{string t;cin >> t >> m.second;m.first = types[t];
}
type->maintain();
cout << type->size << ' ' << type->indent << '\n';
操作 2:定义元素

根据「基本思路」中给出的做法,维护当前第一个可分配的地址和顶层元素列表

LL cur_addr = 0LL;
vector<Object> toplevel_objects;

Object 的定义:

struct Object
{DataType* type; // 类型string name; // 名称LL addr; // 地址
};

计算地址并保存元素

Object obj;
string t;
cin >> t >> obj.name; // 输入
obj.type = types[t]; // 找到类型指针
obj.addr = obj.type->shift(cur_addr); // 对齐
cur_addr = obj.addr + obj.type->size; // 更新可分配的地址
toplevel_objects.push_back(obj); // 保存元素

输出元素地址

cout << obj.addr << '\n';
操作 3:访问元素

定义一个辅助函数,类似于 Python 中的 split(),将一个字符串根据指定分隔符分成若干段:

inline void split(const string& s, char sep, vector<string>& res)
{string t;for(char c: s)if(c == sep)res.push_back(t), t.clear();else t += c;res.push_back(t);
}

处理字符串并找到顶层元素

// 读入
string s;
cin >> s;
// 分割
vector<string> ord;
split(s, '.', ord);
// 根据名称匹配顶层元素
LL addr;
DataType* type;
for(auto& obj: toplevel_objects)if(obj.name == ord[0]){addr = obj.addr;type = obj.type;break;}

逐层向下,计算地址

// ord[0] 对应顶层元素名称,删掉
ord.erase(ord.begin());
// 逐层向下遍历
for(string& s: ord)for(auto& m: type->members){addr = m.first->shift(addr); // 地址对齐if(m.second == s) // 名称匹配{type = m.first; // 找到下一层,向下遍历break;}addr += m.first->size; // 地址移到下一个元素}

输出最终地址

cout << addr << '\n';
操作 4:访问地址

同操作 3,先找到顶层元素

LL addr;
cin >> addr;
if(addr >= cur_addr) // 大于最高有效地址,直接挂掉
{cout << "ERR\n";continue;
}
DataType* type = nullptr;
LL f_addr = 0LL; // 当前考察的地址
string res; // 结果字符串
for(auto& obj: toplevel_objects)
{if(addr < obj.addr) goto bad; // 特判由于对齐导致的地址无效if(addr < obj.addr + obj.type->size) // 地址在当前范围内,记录结果{type = obj.type;res = obj.name;f_addr = obj.addr;break;}
}

向下寻找并输出

// 循环条件:(1) 地址有效 (2) 不是基本类型(类型有成员)
while(addr < f_addr + type->actual_size && !type->members.empty())for(auto& m: type->members){f_addr = m.first->shift(f_addr); // 对齐if(addr < f_addr) goto bad; // 特判,同上if(addr < f_addr + m.first->size){type = m.first;res.push_back('.');res += m.second;break;}f_addr += m.first->size;}
if(addr < f_addr + type->actual_size) cout << res << '\n'; // 地址有效则输出结果
else cout << "ERR\n"; // 地址无效
continue;
bad: cout << "ERR\n"; // 前面使用的 bad 标签

完整代码

下面是赛时代码,也是前面讲解中使用的:

#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
#include <algorithm>
using namespace std;inline void setmax(int& x, int y)
{if(x < y) x = y;
}using LL = long long;struct DataType
{const string name;LL size, actual_size;int indent;vector<pair<DataType*, string>> members;inline DataType(const string& n): name(n) {}inline LL shift(LL addr){return addr % indent? (addr / indent + 1) * indent: addr;}inline void maintain(){size = indent = 0;for(const auto& m: members){setmax(indent, m.first->indent);size = m.first->shift(size) + m.first->size;}actual_size = size;size = shift(size);}
};struct Object
{DataType* type;string name;LL addr;
};inline void split(const string& s, char sep, vector<string>& res)
{string t;for(char c: s)if(c == sep)res.push_back(t), t.clear();else t += c;res.push_back(t);
}int main()
{ios::sync_with_stdio(false); cin.tie(nullptr);unordered_map<string, DataType*> types;auto add_base_type = [&](string name, int size) -> void {DataType* t = new DataType(name);t->size = t->indent = t->actual_size = size;types[name] = t;};add_base_type("byte", 1);add_base_type("short", 2);add_base_type("int", 4);add_base_type("long", 8);int q;cin >> q;vector<Object> toplevel_objects;LL cur_addr = 0LL;while(q--){int op;cin >> op;if(op == 1){string s;int k;cin >> s >> k;DataType* type = new DataType(s);types[s] = type;type->members.resize(k);for(auto& m: type->members){string t;cin >> t >> m.second;m.first = types[t];}type->maintain();cout << type->size << ' ' << type->indent << '\n';}else if(op == 2){Object obj;string t;cin >> t >> obj.name;obj.type = types[t];obj.addr = obj.type->shift(cur_addr);cur_addr = obj.addr + obj.type->size;toplevel_objects.push_back(obj);cout << obj.addr << '\n';}else if(op == 3){string s;cin >> s;vector<string> ord;split(s, '.', ord);LL addr;DataType* type;for(auto& obj: toplevel_objects)if(obj.name == ord[0]){addr = obj.addr;type = obj.type;break;}ord.erase(ord.begin());for(string& s: ord)for(auto& m: type->members){addr = m.first->shift(addr);if(m.second == s){type = m.first;break;}addr += m.first->size;}cout << addr << '\n';}else // op == 4{LL addr;cin >> addr;if(addr >= cur_addr){cout << "ERR\n";continue;}DataType* type = nullptr;LL f_addr = 0LL;string res;for(auto& obj: toplevel_objects){if(addr < obj.addr) goto bad;if(addr < obj.addr + obj.type->size){type = obj.type;res = obj.name;f_addr = obj.addr;break;}}while(addr < f_addr + type->actual_size && !type->members.empty())for(auto& m: type->members){f_addr = m.first->shift(f_addr);if(addr < f_addr) goto bad;if(addr < f_addr + m.first->size){type = m.first;res.push_back('.');res += m.second;break;}f_addr += m.first->size;}if(addr < f_addr + type->actual_size) cout << res << '\n';else cout << "ERR\n";continue;bad: cout << "ERR\n";}}for(auto it=types.begin(); it!=types.end(); it++)delete it->second;return 0;
}

程序共计 180 180 180 行,长度 4.64 K B 4.64\mathrm{KB} 4.64KB,运行用时 73 m s 73\mathrm{ms} 73ms

实际上 Object 的定义没有必要,也不需要存储每个顶层元素的地址,同时还可以稍加压行:

#include <bits/stdc++.h>
using namespace std;using LL = long long;struct DataType {const string name;LL size, actual_size;int indent;vector<pair<DataType*, string>> members;inline DataType(const string& n): name(n) {}inline LL shift(LL addr) {return addr % indent? (addr / indent + 1) * indent: addr;}inline void maintain() {size = indent = 0;for(const auto& m: members){indent = max(indent, m.first->indent);size = m.first->shift(size) + m.first->size;}actual_size = size;size = shift(size);}
};inline void split(const string& s, char sep, vector<string>& res) {string t;for(char c: s)if(c == sep) res.push_back(t), t.clear();else t += c;res.push_back(t);
}int main() {ios::sync_with_stdio(false); cin.tie(nullptr);unordered_map<string, DataType*> types;auto add_base_type = [&](string name, int size) -> void {DataType* t = new DataType(name);t->size = t->indent = t->actual_size = size;types[name] = t;};add_base_type("byte", 1);add_base_type("short", 2);add_base_type("int", 4);add_base_type("long", 8);int q;cin >> q;vector<pair<DataType*, string>> toplevel_objects;LL cur_addr = 0LL;while(q--) {int op;cin >> op;if(op == 1) {string s;int k;cin >> s >> k;DataType* type = new DataType(s);types[s] = type;type->members.resize(k);for(auto& m: type->members) {string t;cin >> t >> m.second;m.first = types[t];}type->maintain();cout << type->size << ' ' << type->indent << '\n';}else if(op == 2) {string t, name;cin >> t >> name;DataType* type = types[t];cur_addr = type->shift(cur_addr);cout << cur_addr << '\n';cur_addr += type->size;toplevel_objects.emplace_back(type, name);}else if(op == 3) {string s;cin >> s;vector<string> ord;split(s, '.', ord);LL addr = 0LL;DataType* type;for(auto& obj: toplevel_objects) {addr = obj.first->shift(addr);if(obj.second == ord[0]) {type = obj.first;break;}addr += obj.first->size;}ord.erase(ord.begin());for(string& s: ord)for(auto& m: type->members) {addr = m.first->shift(addr);if(m.second == s) {type = m.first;break;}addr += m.first->size;}cout << addr << '\n';}else {LL addr;cin >> addr;if(addr >= cur_addr) {cout << "ERR\n";continue;}DataType* type = nullptr;LL f_addr = 0LL;string res;for(auto& obj: toplevel_objects) {f_addr = obj.first->shift(f_addr);if(addr < f_addr) goto bad;if(addr < f_addr + obj.first->size) {type = obj.first;res = obj.second;break;}f_addr += obj.first->size;}while(addr < f_addr + type->actual_size && !type->members.empty())for(auto& m: type->members) {f_addr = m.first->shift(f_addr);if(addr < f_addr) goto bad;if(addr < f_addr + m.first->size) {type = m.first;res.push_back('.');res += m.second;break;}f_addr += m.first->size;}if(addr < f_addr + type->actual_size) cout << res << '\n';else cout << "ERR\n";continue;bad: cout << "ERR\n";}}for(auto it=types.begin(); it!=types.end(); it++)delete it->second;return 0;
}

这样只有 146 146 146 行, 4.51 K B 4.51\mathrm{KB} 4.51KB

不过个人觉得写个 Object 更清楚,所以讲解的时候就没改啦~

后记

算法固然重要,但是编码能力也很重要!强烈建议各位 OIer 重视大模拟,不在这种题上挂分~

写大模拟需要注意的几个点:

  • 变量名写清楚,全写 abcd 到后面自己都不知道是啥,没法调试
  • 该用指针就用指针,不要害怕,用多了会发现真的很好用
  • 适当使用类和结构体,尽量不要全部使用 int 数组
  • 时间复杂度允许的情况下,可读性比性能重要!!(比如本题没有使用二分查找)

祝大家在 NOIP 2023 取得好成绩!求赞qwq

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/192301.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

爬虫学习(一)

文章目录 文件目录结构打开文件操作 爬取网页的理解尝试 文件目录结构 打开文件操作 爬取网页的理解尝试 这个放回值为请求正常

系统架构设计-权限模块的设计

系统架构-权限模块的设计 如何评估一个研发人员技术水平&#xff0c;在大部分的情况下不是看其完成业务代码的好坏&#xff0c;更多的时候还是需要看这个研发人员从零构建一个完整项目的能力&#xff0c;在大公司中这样的机会可能相对较少&#xff0c;大部分的时间里都是对现有…

【数据库】数据库多种锁模式,共享锁、排它锁,更新锁,增量锁,死锁消除与性能优化

多种锁模式的封锁系统 ​专栏内容&#xff1a; 手写数据库toadb 本专栏主要介绍如何从零开发&#xff0c;开发的步骤&#xff0c;以及开发过程中的涉及的原理&#xff0c;遇到的问题等&#xff0c;让大家能跟上并且可以一起开发&#xff0c;让每个需要的人成为参与者。 本专栏会…

2024年美国大学生数学建模竞赛(MCM/ICM)论文写作方法指导

一、前言 谈笑有鸿儒&#xff0c;往来无白丁。鸟宿池边树&#xff0c;僧敲月下门。士为知己者死&#xff0c;女为悦己者容。吴楚东南坼&#xff0c;乾坤日夜浮。剪不断&#xff0c;理还乱&#xff0c;是离愁&#xff0c;别是一番滋味在心头。 重要提示&#xff1a;优秀论文的解…

LeeCode前端算法基础100题(5)- 最长公共前缀

一、问题详情: 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀,返回空字符串 ""。 示例 1: 输入:strs = ["flower","flow","flight"] 输出:"fl" 示例 2: 输入:strs = ["dog",&quo…

唯创知音WT2605C语音芯片MP3音频IC:轻松实现指令随机播放与无缝循环播放等功能

在现代化的电子产品中&#xff0c;音频功能的重要性日益凸显。无论是智能家居、玩具、医疗设备还是仪器仪表&#xff0c;富有吸引力的音效与语音提示都能显著提升用户体验。唯创知音WT2605C语音芯片MP3音频IC便是为了满足这一需求而诞生的&#xff0c;它具备指令随机播放、无缝…

矩阵代数与MATLAB实现(特征值、广义特征值、酋矩阵、奇异值、托普利兹矩阵、汉克尔矩阵、范德蒙矩阵、)

矩阵代数的相关知识 目录 一、特征值与特征向量 1、特征值与特征向量 2、MATLAB计算 二、广义特征值与广义特征向量 1、广义特征值与广义特征向量 2、MATLAB计算 三、酋矩阵 1、酋矩阵 2、MATLAB计算 四、矩阵的奇异值分解 1、奇异值 2、MATLAB计算 五、托普利兹矩…

HuggingFace学习笔记--BitFit高效微调

1--BitFit高效微调 BitFit&#xff0c;全称是 bias-term fine-tuning&#xff0c;其高效微调只去微调带有 bias 的参数&#xff0c;其余参数全部固定&#xff1b; 2--实例代码 from datasets import load_from_disk from transformers import AutoTokenizer, AutoModelForCaus…

python每日一题——20旋转图像

题目 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]]…

【每日OJ —— 226. 翻转二叉树】

每日OJ —— 226. 翻转二叉树 1.题目&#xff1a;226. 翻转二叉树2.解法2.1.算法讲解2.2.代码实现2.3.代码提交通过展示 1.题目&#xff1a;226. 翻转二叉树 2.解法 2.1.算法讲解 我们从根节点开始&#xff0c;递归地对树进行遍历&#xff0c;并从叶子节点先开始翻转。如果当前…

持续集成交付CICD:CentOS 7 安装 Sonarqube9.6

目录 一、实验 1.CentOS 7 安装 Sonarqube9.6 二、问题 1.安装postgresql13服务端报错 2.postgresql13创建用户报错 一、实验 1.CentOS 7 安装 Sonarqube9.6 &#xff08;1&#xff09;下载软件及依赖包 ①Sonarqube9.6下载地址 https://binaries.sonarsource.com/Dis…

深度学习之基于yolov3学生课堂行为及专注力检测预警监督系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 深度学习技术在学生课堂行为及专注力检测预警监督系统的应用是一项极具挑战性和创新性的研究领域。利用YOLOv3&…

Docker常见命令介绍

命令说明 docker pull 拉取镜像 docker push 推送镜像到DockerRegistry docker images 查看本地镜像 docker rmi 删除本地镜像 docker run 创建并运行容器&#xff08;不能重复创建&#xff09; docker stop 停止指定容器 docker start 启动指定容器 docker rest…

设计模式-结构型模式之外观设计模式

文章目录 七、外观模式 七、外观模式 外观模式&#xff08;Facade Pattern&#xff09;隐藏系统的复杂性&#xff0c;并向客户端提供了一个客户端可以访问系统的接口。它向现有的系统添加一个接口&#xff0c;来隐藏系统的复杂性。 这种模式涉及到一个单一的类&#xff0c;该类…

揭秘原型链:探索 JavaScript 面向对象编程的核心(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

解决ubuntu编译aosp报错问题:error while loading shared libraries: libncurses.so.5

报错代码&#xff1a; or directory [ 0% 1036/154449] bc: libclcore.bc < frameworks/rs/driver/runtime/rs_quaternio FAILED: out/target/product/emulator_x86_64/obj/RENDERSCRIPT_BITCODE/libclcore.bc _intermediates/rs_quaternion.bc /bin/bash -c "PWD/pro…

第九节HarmonyOS 常用基础组件2-Image

一、组件介绍 组件&#xff08;Component&#xff09;是界面搭建与显示的最小单位&#xff0c;HarmonyOS ArkUI声名式为开发者提供了丰富多样的UI组件&#xff0c;我们可以使用这些组件轻松的编写出更加丰富、漂亮的界面。 组件根据功能可以分为以下五大类&#xff1a;基础组件…

SmartSoftHelp8,Web前端性能提升,js,css,html 优化压缩工具

Web前端js&#xff0c;css&#xff0c;html 优化压缩工具 提高web 前端性能&#xff0c;访问速度优化专业工具 CSS&#xff0c;js&#xff0c;html 单文件&#xff0c;多文件 单个&#xff0c;批量压缩优化 web前端优化&#xff1a;减少空格&#xff0c;体积压缩&#xff0…

基于算能的国产AI边缘计算盒子8核心A53丨17.6Tops算力

边缘计算盒子 8核心A53丨17.6Tops算力 ● 可提供17.6TOPS&#xff08;INT8&#xff09;的峰值计算能力、2.2TFLOPS&#xff08;FP32&#xff09;的高精度算力&#xff0c;单芯片最高支持32路H.264 & H.265的实时解码能力。 ● 适配Caffe/TensorFlow/MxNet/PyTorch/ ONNX/…

Python 全栈体系【四阶】(一)

四阶&#xff1a;机器学习 - 深度学习 第一章 numpy 一、numpy 概述 Numerical Python&#xff0c;数值的 Python&#xff0c;补充了 Python 语言所欠缺的数值计算能力。 Numpy 是其它数据分析及机器学习库的底层库。 Numpy 完全标准 C 语言实现&#xff0c;运行效率充分优…