P9754 [CSP-S 2023] 结构体 题解
首先,我们需要想清楚要维护哪些信息,把每一种类型(包括基本类型)用结构体维护,里面存:
- 类型的对齐规则
- 占用长度
- 元素个数
- 每个元素的名字、起始位置、类型
- 元素名到编号的映射
struct node{
int dq;//对齐规则
ll sz;//长度
int num;//data numbers
string nme[105];//data name
map<string,int>id;
ll st[105];//start pos
int typ[105];//data type
}a[105];
对于基本类型可以一开始就初始化。同时,我们可以把 \(a_0\) 当做整个空间的结构体,这样会好写很多。
做好准备工作就可以看操作了。
操作一
定义一个结构体类型。相当于类型个数 \(cnt++\),在此,我们需要把 \(a_{cnt}\) 初始化,于是封装一个 \(add\) 函数:
void add(int idx,string mz,ll cd,int dqq){//idx 元素类型 cd 占用长度 dqq 元素的对齐规则
nme[++num]=mz;
typ[num]=idx;
id[mz]=num;
if(sz%dqq)sz=(sz/dqq+1ll)*dqq;//对齐上取整
st[num]=sz;
sz+=cd;
dq=max(dq,dqq);
}
注意:在结构体内部对齐时只根当前元素的对其要求有关,与结构体的对齐要求无关。只有最后一个元素的结尾位置要为对齐要求的倍数(虽然我感觉题面不是这个意思,但是形式化题面确实是这样写的)。输出直接输出就好了。
操作二
定义一个元素。相当于在 \(a_0\) 里加一个元素,调用 \(a_0.add\) 即可。输出 \(a_0.st[a_0.num]\)(\(a_0.num\) 即为当前加的元素的编号)。
操作三
访问某个元素。因为有很多点夹在里面,因此可以用点把字符串分开,再一个一个找。具体地,从左往右获取每个元素(相当于从外到内),每次加上这个元素在他的结构体中的起始位置(因为每个结构体是单独存的,都是从 \(0\) 开始,因此要累加),然后进到这个元素的结构体中继续向下寻找即可。
操作四
访问某个内存地址。在 \(a_0\) 中找到最后一个起始地址 \(\leq\) 输入地址的地方,他的下一个元素的起始地址一定就大于访问的地址了,也就一定不在那里面。因此访问地址在当前位置。每次找到位置后就用访问的 \(addr\) 减去当前的起始位置,再将结构体更新为当前元素的结构体并进入其中继续寻找。判断 \(ERR\) 的话就在每次进入时看一下 \(addr\) 是否 \(\ge\) 当前结构体的 \(size\) 了,如果比它大那就证明 \(addr\) 没有被填到,因此就错误了。答案在访问途中加上元素名即可。
其余细节见注释。
code
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int q;
int cnt=4;//cnt type numbers
map<string,int>type;//记录每种类型的编号
struct node{
int dq;
ll sz;
int num;//data numbers
string nme[105];//data name
map<string,int>id;
ll st[105];//start pos
int typ[105];//data type
void add(int idx,string mz,ll cd,int dqq){
nme[++num]=mz;
typ[num]=idx;//存它类型的编号(即结构体编号)方便快速访问
id[mz]=num;
if(sz%dqq)sz=(sz/dqq+1ll)*dqq;
st[num]=sz;
sz+=cd;
dq=max(dq,dqq);
}
void calc(){
if(sz%dq)sz=(sz/dq+1ll)*dq;//结构体内对齐
}
}a[105];
void solve(){
int op,k,ard;
string s,t;
cin>>op;
if(op==1){
cin>>s>>k;
type[s]=++cnt;
string tpe,nm;
for(int i=1;i<=k;++i){
cin>>tpe>>nm;
int now=type[tpe];
a[cnt].add(now,nm,a[now].sz,a[now].dq);
}
a[cnt].calc();
cout<<a[cnt].sz<<" "<<a[cnt].dq<<endl;
}
else if(op==2){
cin>>t>>s;
int now=type[t];
a[0].add(now,s,a[now].sz,a[now].dq);
cout<<a[0].st[a[0].num]<<endl;
}
else if(op==3){
cin>>s;
string tmp="";
int now=0;
ll res=0;
s=s+'.';
for(int i=0;i<s.size();++i){
if(s[i]=='.'){
int pos=a[now].id[tmp];
res+=a[now].st[pos];//累加答案
now=a[now].typ[pos];//进入当前结构体继续寻找
tmp="";
}
else tmp+=s[i];
}
cout<<res<<endl;
}
else if(op==4){
ll adr;
cin>>adr;
string res="";
int now=0;
while(1){
if(adr>=a[now].sz){//填不到了
res="ERR";
break;
}
if(now>=1&&now<=4)break;//已经进入基本类型,就相当于递归完了
for(int i=1;i<=a[now].num;++i){
if(a[now].st[i+1]>adr||i==a[now].num){//寻找
adr-=a[now].st[i];
res+=a[now].nme[i];
now=a[now].typ[i];
if(now>4)res=res+'.';//不是基本类型就需要继续找下去
break;
}
}
}
cout<<res<<endl;
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
type["byte"]=1;a[1].sz=a[1].dq=1;//基本类型用1、2、3、4表示
type["short"]=2;a[2].sz=a[2].dq=2;
type["int"]=3;a[3].sz=a[3].dq=4;
type["long"]=4;a[4].sz=a[4].dq=8;
cin>>q;
while(q--)solve();
return 0;
}
结语
写大模拟一定要先理清思路,再打代码。并且要时刻保持头脑清醒。不然就会因为一些细节没想明白而把自己心态写炸,错失考场上 AC 的机会。还是得多练啊。