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

发布时间 2024-01-07 19:40:44作者: SunsetLake

首先,我们需要想清楚要维护哪些信息,把每一种类型(包括基本类型)用结构体维护,里面存:

  • 类型的对齐规则
  • 占用长度
  • 元素个数
  • 每个元素的名字、起始位置、类型
  • 元素名到编号的映射
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 的机会。还是得多练啊。