P9754 [CSP-S 2023] 结构体 题解
大模拟的话,大家应该都会,主要就是容易写挂。
操作 1
先理解什么叫做对齐规则。这点我们以样例 2 进行解释:
struct a { int aa; short ab; long ac; byte ad; }
那么 aa
占据了 \(0\text{~}3\) 字节的地址,ab
占据了 \(4\text{~}5\) 字节的地址,因为 ac
对齐方式为 8
,所以要从 \(8\) 字节开始,占据了 \(8\text{~}15\) 字节的地址,ad
占据了第 \(16\) 字节的地址,共占据 \(0\text{~}16\) 共 \(17\) 个字节,最后面因为结构体中最大的对齐方式为 \(8\),所以整个结构体占用了第 \(0\text{~}23\) 字节的地址。
考虑把每个结构体抽象成一个节点,结构体成员指向所对应的其他节点,那么最后整个图就是一个拓扑图,以下面为例:
struct ddd { short a; int b; short c; }; struct eee { d a; int b; d c; }; e f;
我们再维护每个节点对于该结构体的内存位置即可。那么操作 1 就顺理成章的完成了。
代码:
void op_1() { node v; ll k; cin >> v.name >> k; for(ll i = 1; i <= k; i++) { element one; string type; cin >> type >> one.name; one.id = name2id(type); // 通过类型名称找到节点下标 v.align(structs[one.id].kind); // 内存对齐 one.st = v.ti; // 内存起始位置(对于该结构体) v.ti += structs[one.id].siz; one.ed = v.ti - 1; // 内存结束位置 v.kind = max(v.kind, structs[one.id].kind); v.son.push_back(one); } v.align(v.kind); // 最后也要对齐 v.siz = v.ti; structs.push_back(v); cout << v.siz << ' ' << v.kind << '\n'; }
操作2
我们再写一个操作纯纯增加码量,其实我们可以把全局变量看做一个大结构体,每次定义全局变量就是在这个结构体内定义成员。
代码:
void op_2() { element one; string type; cin >> type >> one.name; one.id = name2id(type); rt.align(structs[one.id].kind); one.st = rt.ti; rt.ti += structs[one.id].siz; one.ed = rt.ti - 1; rt.kind = max(rt.kind, structs[one.id].kind); rt.son.push_back(one); cout << one.st << '\n'; }
操作 3
纯纯搜索即可,我们只需要在每次搜索加上一个偏移量 \(\text{shift}\) 即可:
void op_3() { string path; cin >> path; vector<string> path_vec; string _ = ""; for(auto v : path) { if(v == '.') { path_vec.push_back(_); _ = ""; } else { _.push_back(v); } } path_vec.push_back(_); path2addr(0, 0, 0, path_vec); } void path2addr(ll x, ll pos, ll shift, vector<string> s) { for(auto v : structs[pos].son) { if(v.name == s[x]) { if(x == s.size() - 1) { cout << shift + v.st << '\n'; return; } path2addr(x+1, v.id, shift + v.st, s); // 加上偏移量 return; } } }
操作 4
类比搜索树的写法,我们每次进入一个元素就减去前面的内存即可:
void op_4() { ll addr; cin >> addr; addr2path(addr, 0, ""); } void addr2path(ll addr, ll pos, string path) { for(auto v : structs[pos].son) { if(v.st <= addr && addr <= v.ed) { // 注意左右都要满足 if(structs[v.id].flag) { // 注意输出时 path 可能为空,痛失 100pts if(path == "") cout << v.name << '\n'; else cout << path + '.' + v.name << '\n'; return; } if(path == "") addr2path(addr - v.st, v.id, v.name);// 减去前面的内存位置,得到关于该结构体的内存位置 else addr2path(addr - v.st, v.id, path + '.' + v.name); return; } } cout << "ERR\n"; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!