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

发布时间 2023-10-26 13:21:30作者: ZnPdCo

大模拟的话,大家应该都会,主要就是容易写挂。

操作 1

先理解什么叫做对齐规则。这点我们以样例 2 进行解释:

struct a {
	int aa;
	short ab;
	long ac;
	byte ad;
}

那么 aa 占据了 \(0\text{~}3\) 字节的地址,ab 占据了 \(4\text{~}5\) 字节的地址,因为 ac对齐方式为 8,所以要从 \(8\) 字节开始,占据了 \(8\text{~}15\) 字节的地址,ad 占据了第 \(16\) 字节的地址,共占据 \(0\text{~}16\)\(17\) 个字节,最后面因为结构体中最大的对齐方式为 \(8\),所以整个结构体占用了第 \(0\text{~}23\) 字节的地址。

考虑把每个结构体抽象成一个节点,结构体成员指向所对应的其他节点,那么最后整个图就是一个拓扑图,以下面为例:

struct d {
    short a;
    int b;
    short c;
};
struct e {
    d a;
    int b;
    d c;
};
e f;
graph TD e((e)) --1--> d((d)) e --2--> int((int)) e --3--> d d --1--> short((short)) d --2--> int((int)) d --3--> short((short))

我们再维护每个节点对于该结构体的内存位置即可。那么操作 1 就顺理成章的完成了。

代码:

void op_1() {
	node v;
	ll k;
	cin >> v.name >> k;
	
	for(ll i = 1; i <= k; i++) {
		element one;
		string type;
		cin >> type >> one.name;
		one.id = name2id(type);						// 通过类型名称找到节点下标 
		
		v.align(structs[one.id].kind);				// 内存对齐 
		one.st = v.ti;								// 内存起始位置(对于该结构体) 
		v.ti += structs[one.id].siz;
		one.ed = v.ti - 1;							// 内存结束位置  
		
		v.kind = max(v.kind, structs[one.id].kind);
		v.son.push_back(one);
	}
	v.align(v.kind);								// 最后也要对齐 
	v.siz = v.ti;
	
	structs.push_back(v);
	
	cout << v.siz << ' ' << v.kind << '\n';
}

操作2

我们再写一个操作纯纯增加码量,其实我们可以把全局变量看做一个大结构体,每次定义全局变量就是在这个结构体内定义成员。

代码:

void op_2() {
	element one;
	string type;
	cin >> type >> one.name;
	one.id = name2id(type);
	
	rt.align(structs[one.id].kind);
	one.st = rt.ti;
	rt.ti += structs[one.id].siz;
	one.ed = rt.ti - 1;
	
	rt.kind = max(rt.kind, structs[one.id].kind);
	rt.son.push_back(one);
	
	cout << one.st << '\n';
}

操作 3

纯纯搜索即可,我们只需要在每次搜索加上一个偏移量 \(\text{shift}\) 即可:

void op_3() {
	string path;
	cin >> path;
	
	vector<string> path_vec;
	string _ = "";
	
	for(auto v : path) {
		if(v == '.') {
			path_vec.push_back(_);
			_ = "";
		} else {
			_.push_back(v);
		}
	}
	path_vec.push_back(_);
	
	path2addr(0, 0, 0, path_vec);
}
void path2addr(ll x, ll pos, ll shift, vector<string> s) {
	for(auto v : structs[pos].son) {
		if(v.name == s[x]) {
			if(x == s.size() - 1) {
				cout << shift + v.st << '\n';
				return;
			}
			path2addr(x+1, v.id, shift + v.st, s);	// 加上偏移量 
			return;
		}
	}
}

操作 4

类比搜索树的写法,我们每次进入一个元素就减去前面的内存即可:

void op_4() {
	ll addr;
	cin >> addr;
	
	addr2path(addr, 0, "");
}
void addr2path(ll addr, ll pos, string path) {
	for(auto v : structs[pos].son) {
		if(v.st <= addr && addr <= v.ed) {			// 注意左右都要满足 
			if(structs[v.id].flag) {				// 注意输出时 path 可能为空,痛失 100pts 
				if(path == "") cout << v.name << '\n';
				else cout << path + '.' + v.name << '\n';
				return;
			}
			if(path == "")
				addr2path(addr - v.st, v.id, v.name);// 减去前面的内存位置,得到关于该结构体的内存位置 
			else
				addr2path(addr - v.st, v.id, path + '.' + v.name);
			return;
		}
	}
	cout << "ERR\n";
}

完整代码