P9754题解
2023 CPS-S T3 题解
前言:
TE 来抢题解啦。
感谢 CCF 出了一道大模拟放在第三题,使得我逆天改命最后两分钟过了这题,然后变成两周后高贵的 LA 群友。
我发现题解普遍是写的和我思路几乎一模一样,是标准的结构体指针写法。我的同学们还说我写结构体指针是脑残发作,这下这下了。
本人评价该题为正常难度蓝题。
解法:
准备工作
首先搞个结构体。然后存自己的别名和类型名,按照形式化题意维护相对偏移量,对齐参数,长度。用一个 vector 存自己的儿子指针。
初始化的方式采用一个 map 来存储默认结构体。别忘了存他给的四种初始数据类型。
当我们需要创造一个新的结构体时,就先声明一个空的范例结构体,按照题意给他分配对应的儿子节点(别忘了改儿子的别名),然后不断更新儿子的偏移量和自己的对齐参数,最后更新自己的长度和类型名字,存到 map 中去。这中间不会动自己的别名和相对偏移量,默认为空,要注意。
代码:
struct jiegou{
string name,typ; //别名和类型
int len,dq,py; //长度,对齐,相对父亲的偏移量
vector<jiegou *>son; //儿子(嵌套的结构体)
jiegou(){ py=len=dq=0; name=typ="";}
jiegou(int len,string typ){
this->py=0; this->len=len; this->dq=len; this->typ=typ;
}
};
map<string,jiegou>mp; //用于初始化结构体的一个map
int sqz(int a,int b){ return a/b+(int)(a%b>0); } //顾名思义,上取整
void build(string x,int k){
jiegou tmp;
tmp.typ=x;
int py=0;
jiegou *lst;
rep(i,1,k){
string typ,alia; cin>>typ>>alia;
jiegou *tmp1=new jiegou(mp[typ]); tmp1->name=alia;
tmp.dq=max(tmp1->dq,tmp.dq);
if(i>1){
tmp1->py=tmp1->dq*sqz(lst->py+lst->len,tmp1->dq);
}
tmp.son.push_back(tmp1); lst=tmp1;
}
tmp.len=tmp.dq*sqz(lst->len+lst->py,tmp.dq);
mp[x]=tmp;
cout<<tmp.len<<" "<<tmp.dq<<endl;
}
加入至内存池
我们可以把内存池当做一个没有别名、类型、长度的树根,然后动态维护它的对齐参数并默认它的偏移量为
代码:
jiegou *tr=new jiegou();
void add(string typ,string nam){
jiegou *tmp=new jiegou(mp[typ]); tmp->name=nam;
int x,y,st=0;
vector<jiegou *>vec=tr->son; //简化一下,呜呜
if(vec.empty()) x=0,y=0;
else x=(*--vec.end())->py,y=(*--vec.end())->len;
st=sqz(x+y,tmp->dq)*tmp->dq;
tmp->py=st;
tr->son.push_back(tmp);
cout<<st<<endl;
}
索引找内存
直接暴力分割出来每一层索引的名字然后暴力一层一层跳就可以了。这个没啥难度。
int find(string x){
vector<string>vec;
int lst=0,len=x.length();
rep(i,1,len)
if(x[i-1]=='.') vec.push_back(x.substr(lst,i-lst-1)),lst=i;
vec.push_back(x.substr(lst,len-lst)); //暴力分割索引
jiegou *now=tr;
int st=0,py;
rep(i,1,vec.size()){ //暴力一层一层跳
py=st;
rep(j,1,now->son.size()){
jiegou *k=now->son[j-1];
py=st+k->py;
if(k->name==vec[i-1]){
st=py; now=k; break;
}
}
}
return py;
}
内存找索引
递归找一下,暴力一层层跳,递归存一下目前的总偏移量,如果子节点包含这个区间就往下找。
一个小 trick:我的写法如果失配就会在最后一层正好返回一个 ERR,也就是 x.xx.xxx.ERR 的格式,我懒得手写判定就直接判里面有没有子串是 ERR,反正索引都是小写的。具体代码写主函数里面了。
代码:
string getbyadr(jiegou *now,int st,int tar){
string ans="";
rep(i,1,now->son.size()){
jiegou *k=now->son[i-1];
if(st+(k->py)<=tar&&tar<st+(k->py)+(k->len)){
ans=k->name;
if(k->son.size()) ans+="."+getbyadr(k,st+(k->py),tar);
break;
}
}
if(ans.empty()) return "ERR";
else return ans;
}
主函数和宏定义
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define fi first
#define sc second
using namespace std;
#define int long long //为了保险开的,不少人没开ll痛失40分
/*
结构体、变量的定义和各个函数的实现方式省略。
*/
signed main(){
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int n; cin>>n;
mp["long"]=jiegou(8ll,"long");
mp["int"]=jiegou(4ll,"int");
mp["short"]=jiegou(2ll,"short");
mp["byte"]=jiegou(1ll,"byte");
rep(idk,1,n){
int opt; cin>>opt;
if(opt==1){
string x; int y; cin>>x>>y;
build(x,y);
}
else if(opt==2){
string x,y; cin>>x>>y; add(x,y);
}
else if(opt==3){
string x; cin>>x; cout<<find(x)<<endl;
}
else if(opt==4){
int adr; cin>>adr;
string ans=getbyadr(tr,0,adr);
if(ans.find("ERR")!=-1) cout<<"ERR"<<endl;
//一个取巧的方法(因为错误时其实上只有最后一层是ERR)
else cout<<ans<<endl;
}
}
}
收尾:
感谢谷友们对我 OI 事业的支持,感谢家长和南外老师们对我 OI 事业的支持,希望你们看到的时候顶我上去,谢谢!
另外顺便提一句,赛后优化代码可以使代码最短用时小于 6 ms 并且长度小于 2.9 KiB。