按对齐规则计算类型大小和成员偏移,再递归处理路径访问与地址反查。
OJ: luogu
题目 ID: P9754
难度:普及+/提高
标签:模拟递归哈希表
日期: 2026-07-06 08:46
题意
题目要求模拟一种简化的结构体系统,需要支持四类操作:
- 定义结构体类型,并输出这个类型的大小和对齐要求;
- 定义一个全局元素,并输出它的起始地址;
- 根据
a.b.c这样的路径访问成员,输出最内层元素的起始地址; - 给定一个地址,反查它是否落在某个基本类型元素内部,如果是就输出访问路径,否则输出
ERR。
基本类型 byte, short, int, long 的大小和对齐要求分别是 1,2,4,8。结构体类型的对齐要求是所有成员对齐要求的最大值。
思路
这是一道规则模拟题,朴素做法就是按照题意维护类型表、元素表和成员偏移:
cpp
// brute.cpp:小数据朴素模拟,同样按定义顺序计算偏移并递归查找地址。
#include <bits/stdc++.h>
using namespace std;
struct MemberInfo {
string type_name;
string name;
long long offset;
};
struct TypeInfo {
string name;
long long size;
long long align;
bool is_basic;
vector<MemberInfo> members;
map<string, int> member_id;
};
struct ElementInfo {
string type_name;
string name;
long long start;
};
vector<TypeInfo> types;
vector<ElementInfo> elements;
map<string, int> type_id;
map<string, int> element_id;
long long memory_end;
long long align_up(long long x, long long a) {
if (x % a == 0) return x;
return x + (a - x % a);
}
void add_basic_type(const string &name, long long size) {
TypeInfo t;
t.name = name;
t.size = size;
t.align = size;
t.is_basic = true;
type_id[name] = (int)types.size();
types.push_back(t);
}
vector<string> split_path(const string &s) {
vector<string> result;
string cur;
for (int i = 0; i < (int)s.size(); i++) {
if (s[i] == '.') {
result.push_back(cur);
cur.clear();
} else {
cur.push_back(s[i]);
}
}
result.push_back(cur);
return result;
}
bool find_addr_in_type(int tid, long long base, long long addr, const string &path, string &answer) {
if (types[tid].is_basic) {
if (base <= addr && addr < base + types[tid].size) {
answer = path;
return true;
}
return false;
}
for (int i = 0; i < (int)types[tid].members.size(); i++) {
MemberInfo member = types[tid].members[i];
int child_tid = type_id[member.type_name];
long long child_base = base + member.offset;
if (child_base <= addr && addr < child_base + types[child_tid].size) {
return find_addr_in_type(child_tid, child_base, addr, path + "." + member.name, answer);
}
}
return false;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
add_basic_type("byte", 1);
add_basic_type("short", 2);
add_basic_type("int", 4);
add_basic_type("long", 8);
int q;
cin >> q;
while (q--) {
int op;
cin >> op;
if (op == 1) {
string name;
int k;
cin >> name >> k;
TypeInfo t;
t.name = name;
t.size = 0;
t.align = 1;
t.is_basic = false;
long long cur = 0;
for (int i = 0; i < k; i++) {
string type_name, member_name;
cin >> type_name >> member_name;
int tid = type_id[type_name];
cur = align_up(cur, types[tid].align);
MemberInfo member;
member.type_name = type_name;
member.name = member_name;
member.offset = cur;
t.member_id[member_name] = (int)t.members.size();
t.members.push_back(member);
cur += types[tid].size;
t.align = max(t.align, types[tid].align);
}
t.size = align_up(cur, t.align);
type_id[name] = (int)types.size();
types.push_back(t);
cout << t.size << ' ' << t.align << '\n';
} else if (op == 2) {
string type_name, name;
cin >> type_name >> name;
int tid = type_id[type_name];
ElementInfo e;
e.type_name = type_name;
e.name = name;
e.start = align_up(memory_end, types[tid].align);
memory_end = e.start + types[tid].size;
element_id[name] = (int)elements.size();
elements.push_back(e);
cout << e.start << '\n';
} else if (op == 3) {
string path;
cin >> path;
vector<string> parts = split_path(path);
int eid = element_id[parts[0]];
long long addr = elements[eid].start;
string cur_type = elements[eid].type_name;
for (int i = 1; i < (int)parts.size(); i++) {
int tid = type_id[cur_type];
int mid = types[tid].member_id[parts[i]];
MemberInfo member = types[tid].members[mid];
addr += member.offset;
cur_type = member.type_name;
}
cout << addr << '\n';
} else {
long long addr;
cin >> addr;
string answer;
bool ok = false;
for (int i = 0; i < (int)elements.size(); i++) {
int tid = type_id[elements[i].type_name];
long long l = elements[i].start;
long long r = l + types[tid].size;
if (l <= addr && addr < r) {
ok = find_addr_in_type(tid, l, addr, elements[i].name, answer);
break;
}
}
if (ok) cout << answer << '\n';
else cout << "ERR\n";
}
}
return 0;
}由于操作数和成员数都不大,直接模拟已经可以作为正解。需要注意的是,结构体内部和全局元素之间都要进行对齐。
定义结构体时,从偏移 0 开始依次放置成员。设当前已经用到的位置为 cur,新成员的对齐要求为 a,那么它的起始偏移是:
text
offset = align_up(cur, a)放完后令:
text
cur = offset + member_size所有成员处理完后,结构体大小还要再对齐到结构体自身的对齐要求。
定义全局元素时同理,把当前全局内存末尾 memory_end 对齐到该元素类型的对齐要求,得到元素起始地址。
路径访问 x.ba.ab 时:
- 先找到全局元素
x的起始地址和类型; - 每访问一层成员,就把当前地址加上该成员在当前类型里的偏移;
- 当前类型变成该成员的类型。
地址反查要递归进入结构体:
- 如果当前类型是基本类型,并且地址落在它的范围内,就找到了答案;
- 如果当前类型是结构体,就检查地址落在哪个成员的范围内,再递归进入这个成员;
- 如果地址落在结构体对齐留下的空洞里,就输出
ERR。
代码
cpp
// main.cpp:模拟结构体类型定义、元素定义、路径访问和地址反查。
#include <bits/stdc++.h>
using namespace std;
struct MemberInfo {
string type_name;
string name;
long long offset;
};
struct TypeInfo {
string name;
long long size;
long long align;
bool is_basic;
vector<MemberInfo> members;
map<string, int> member_id;
};
struct ElementInfo {
string type_name;
string name;
long long start;
};
vector<TypeInfo> types;
vector<ElementInfo> elements;
map<string, int> type_id;
map<string, int> element_id;
long long memory_end;
long long align_up(long long x, long long a) {
if (x % a == 0) {
return x;
}
return x + (a - x % a);
}
void add_basic_type(const string &name, long long size) {
TypeInfo t;
t.name = name;
t.size = size;
t.align = size;
t.is_basic = true;
type_id[name] = (int)types.size();
types.push_back(t);
}
vector<string> split_path(const string &s) {
vector<string> result;
string cur;
for (int i = 0; i < (int)s.size(); i++) {
if (s[i] == '.') {
result.push_back(cur);
cur.clear();
} else {
cur.push_back(s[i]);
}
}
result.push_back(cur);
return result;
}
void define_struct_type() {
string name;
int k;
cin >> name >> k;
TypeInfo t;
t.name = name;
t.size = 0;
t.align = 1;
t.is_basic = false;
long long cur = 0;
for (int i = 0; i < k; i++) {
string type_name, member_name;
cin >> type_name >> member_name;
int tid = type_id[type_name];
cur = align_up(cur, types[tid].align);
MemberInfo member;
member.type_name = type_name;
member.name = member_name;
member.offset = cur;
t.member_id[member_name] = (int)t.members.size();
t.members.push_back(member);
cur += types[tid].size;
t.align = max(t.align, types[tid].align);
}
t.size = align_up(cur, t.align);
type_id[name] = (int)types.size();
types.push_back(t);
cout << t.size << ' ' << t.align << '\n';
}
void define_element() {
string type_name, name;
cin >> type_name >> name;
int tid = type_id[type_name];
ElementInfo e;
e.type_name = type_name;
e.name = name;
e.start = align_up(memory_end, types[tid].align);
memory_end = e.start + types[tid].size;
element_id[name] = (int)elements.size();
elements.push_back(e);
cout << e.start << '\n';
}
void query_path() {
string path;
cin >> path;
vector<string> parts = split_path(path);
int eid = element_id[parts[0]];
long long addr = elements[eid].start;
string cur_type = elements[eid].type_name;
for (int i = 1; i < (int)parts.size(); i++) {
int tid = type_id[cur_type];
int mid = types[tid].member_id[parts[i]];
MemberInfo member = types[tid].members[mid];
addr += member.offset;
cur_type = member.type_name;
}
cout << addr << '\n';
}
bool find_addr_in_type(int tid, long long base, long long addr, const string &path, string &answer) {
if (types[tid].is_basic) {
if (base <= addr && addr < base + types[tid].size) {
answer = path;
return true;
}
return false;
}
for (int i = 0; i < (int)types[tid].members.size(); i++) {
MemberInfo member = types[tid].members[i];
int child_tid = type_id[member.type_name];
long long child_base = base + member.offset;
long long child_end = child_base + types[child_tid].size;
if (child_base <= addr && addr < child_end) {
return find_addr_in_type(child_tid, child_base, addr, path + "." + member.name, answer);
}
}
return false;
}
void query_address() {
long long addr;
cin >> addr;
string answer;
for (int i = 0; i < (int)elements.size(); i++) {
int tid = type_id[elements[i].type_name];
long long l = elements[i].start;
long long r = l + types[tid].size;
if (l <= addr && addr < r) {
if (find_addr_in_type(tid, l, addr, elements[i].name, answer)) {
cout << answer << '\n';
} else {
cout << "ERR\n";
}
return;
}
}
cout << "ERR\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
add_basic_type("byte", 1);
add_basic_type("short", 2);
add_basic_type("int", 4);
add_basic_type("long", 8);
int q;
cin >> q;
while (q--) {
int op;
cin >> op;
if (op == 1) {
define_struct_type();
} else if (op == 2) {
define_element();
} else if (op == 3) {
query_path();
} else {
query_address();
}
}
return 0;
}复杂度
设一次结构体最多有 k 个成员,路径深度为 d。
定义结构体需要
由于题目操作数最多 100,整体复杂度远小于限制。空间复杂度为
总结
本题最容易错的是“结构体大小也要按最大对齐要求补齐”,以及地址反查时要区分真正的基本类型占用区间和对齐产生的空洞。
只要为每个类型保存 size/align/成员偏移,四种操作都可以直接从这些信息推出来。