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

前言

在考场上调了 2h+ 还没调出来,并且 T4 也没来得及做。希望看到这段文字的你能避免这样的悲剧。

正文

题目要求动态创建类型,于是我用结构体代表一个 struct(禁止套娃),要新建就 new 出来一个。(最后也没 delete

struct Obj{
    typnam tnam;
    ll len, align;
    std::map<std::string, std::pair<Obj*, ll>> subs;
    Obj():tnam(), len(0), align(0){}
    Obj(std::string nam, ll le=0, ll ali=0):tnam(nam), len(le), align(ali){}
};

由于结构体的名称和变量名称容易混淆,我用 typnam 标注了结构体的名称。

using typnam = std::string;

声明了结构体之后,题目要求根据名称找出一个结构体,我用 std::set 保存。这里也可以用 std::map

struct comp_obj_by_typnam{ bool operator()(Obj *a, Obj *b) const { return a->tnam < b->tnam; } };
std::set<Obj*, comp_obj_by_typnam> decl_objs = {&BByte, &SShort, &IInt, &LLong};
Obj *get_obj_by_tname(typnam &tnam){
    Obj tmp(tnam);
    return *decl_objs.find(&tmp);
}

对于已存在的变量,我用一个 map 保存名称,类型和起始位置。

其实这里也可以新建一个特殊的 struct,就像森林转树要加一个超级根一样。这样可以避免一些重复的代码。

std::map<std::string, std::pair<Obj*, ll>> exist_objs;
ll global_offset = 0;

关于如何计算对齐:

设第 \(i\) 个被定义的元素大小为 \(s_i\),对齐要求为 \(a_i\),起始地址为 \(b_i\);
\(b_1=0\),对于 \(2\le i\)\(b_i\) 为满足 \(b_{i−1}+s_{i−1} \le b_i\)\(a_i\) 整除 \(b_i\) 的最小值。

\(k=b_{i-1}+s_{i-1}\),若 \(k \equiv 0 \pmod {a_i}\),则 \(b_i\) 正好可以放入;

否则,\(b_i = k+a_i-(k \bmod {a_i})\)

ll get_start_addr(ll old_offset, Obj const *me){
    if(old_offset % me->align) old_offset += me->align - old_offset%me->align;
    return old_offset;
}

关于访问某个元素:

读入名称 s 后,可以用 s.substr(0, s.find(".")) 找到 struct 名。

附完整代码,用于 debug:

完整代码
#include <bits/stdc++.h>
#define UP(i,s,e) for(auto i=s; i<e; ++i)
namespace m{ // }{{{
using std::cin; using std::cout;
using typnam = std::string;
using ll = long long;
struct Obj{
    typnam tnam;
    ll len, align;
    std::map<std::string, std::pair<Obj*, ll>> subs;
    Obj():tnam(), len(0), align(0){}
    Obj(std::string nam, ll le=0, ll ali=0):tnam(nam), len(le), align(ali){}
};
Obj BByte = {"byte", 1, 1},
    SShort = {"short", 2, 2},
    IInt = {"int", 4, 4},
    LLong = {"long", 8, 8};
int in;
struct comp_obj_by_typnam{ bool operator()(Obj *a, Obj *b) const { return a->tnam < b->tnam; } };
std::map<std::string, std::pair<Obj*, ll>> exist_objs;
ll global_offset = 0;
std::set<Obj*, comp_obj_by_typnam> decl_objs = {&BByte, &SShort, &IInt, &LLong};
Obj *get_obj_by_tname(typnam &tnam){
    Obj tmp(tnam);
    return *decl_objs.find(&tmp);
}
ll get_start_addr(ll old_offset, Obj const *me){
    if(old_offset % me->align) old_offset += me->align - old_offset%me->align;
    return old_offset;
}
void declare_struct(){
    std::string is; int ik;
    cin >> is >> ik;
    Obj *now = new Obj;
    now->tnam = is;
    ll nowoff = 0;
    UP(i, 0, ik){
        std::string it, in;
        cin >> it >> in;
        Obj *me = get_obj_by_tname(it);
        now->align = std::max(now->align, me->align);
        nowoff = get_start_addr(nowoff, me);
        now->subs.insert({in, {me, nowoff}});
        nowoff += me->len;
    }
    now->len = get_start_addr(nowoff, now);
    decl_objs.insert(now);
    cout << now->len << ' ' << now->align << '\n';
}
void create_obj(){
    std::string it, in;
    cin >> it >> in;
    Obj *now = get_obj_by_tname(it);
    global_offset = get_start_addr(global_offset, now);
    exist_objs.insert({in, {now, global_offset}});
    cout << global_offset << '\n';
    global_offset += now->len;
}
void access_obj_recursive(ll sum_offset, std::string &is, Obj *now){
    auto pl = is.find('.');
    if(pl == std::string::npos){
        cout << sum_offset+now->subs[is].second << '\n';
        return;
    }
    auto &now_info = now->subs[is.substr(0, pl)];
    now = now_info.first;
    is.erase(0, pl+1);
    access_obj_recursive(sum_offset + now_info.second, is, now);
}
void access_obj(){
    std::string is;
    cin >> is;
    auto pl = is.find('.');
    if(pl == std::string::npos){
        cout << exist_objs[is].second << '\n'; 
        return;
    }
    auto &now_info = exist_objs[is.substr(0, pl)];
    Obj *now = now_info.first;
    is.erase(0, pl+1);
    access_obj_recursive(now_info.second, is, now);
}
void access_addr_recursive(ll addr, Obj *now, std::string &ans){
    if(now == &BByte || now == &SShort || now == &IInt || now == &LLong){
        cout << ans << '\n';
        return;
    }
    for(auto &i:now->subs){
        if(i.second.second <= addr && 
                i.second.second + i.second.first->len > addr){
            ans += '.';
            ans += i.first;
            access_addr_recursive(
                    addr-i.second.second,
                    i.second.first,
                    ans);
            return;
        }
    }
    cout << "ERR\n";
}
void access_addr(){
    ll addr; cin >> addr;
    std::string ans;
    for(auto &i:exist_objs){
        if(i.second.second <= addr && 
                i.second.second + i.second.first->len > addr){
            ans = i.first;
            access_addr_recursive(
                    addr-i.second.second,
                    i.second.first,
                    ans);
            return;
        }
    }
    cout << "ERR\n";
}
void work(){
    cin >> in;
    UP(i, 0, in){
        int op;
        cin >> op;
        if(op == 1){
            declare_struct();
        } else if(op == 2){
            create_obj();
        } else if(op == 3){
            access_obj();
        } else {
            access_addr();
        }
    }
}
} // {}}}
int main(){m::work(); return 0; }
posted @ 2023-10-22 13:50  383494  阅读(301)  评论(0)    收藏  举报