Y
K
N
U
F

解题记录说是 | P3695 CYaRon!语

起因

闲的没事找模拟做,发现这个部分分的档次很多,而且好想挺好做,就做了。

是分着部分分做的,而且完全就是那种苦(封装各种实现)尽甘(飞速写完 ihuhorwhile)来的感觉,很爽的。

讲解

首先我们显然发现注释是没有用的,所以略过注释,并且发现我们先读入所有的东西再处理会让ihuhorwhile等变得更好处理,所以我们一次性读到 \(cmd\) 里面,这很好。

while(1) {
	c = getc();
	if(c == '#') {
		while(c != '\n') c = getc();
		cmd += c;
	}
	else cmd.push_back(c);
	if(c == -1) break;
}

然后我们一个一个看操作们。

基础函数们

to__

后面的 __ 是空格的意思,作用为跳到下一个空格等空白字符处。

inline void to__(uint &i) {
	while(cmd[i] != ' ' and cmd[i] != -1 and cmd[i] != '\n') i ++;
}

pass

含义为跳过空格类的无用字符。如 \t\n 之类的。

inline void pass(uint &i) {
	while((cmd[i] == ' ' || cmd[i] == '\n'  || cmd[i] == '\t') and cmd[i] != -1) i ++; 
}

get_str

作用为返回一定量的合法字符,有很多的边界,为\n -1\t:+-,特殊的是返回符号就只返回单个的符号。

inline string get_str(uint &i) {
	string tmp = "";
	pass(i);
	if(cmd[i] == '+' || cmd[i] == '-') 
		return cmd[i++];
	while(cmd[i] != ' ' and cmd[i] != ':' and cmd[i] != '\n' and cmd[i] != -1 and cmd[i] != ',' and cmd[i] != '+' and cmd[i] != '-') 
		tmp.push_back(cmd[i ++]);
	return tmp;
}

in

魔改快读,作用为返回下一个合法的数字。

inline int in(uint &i) { 
	int n = 0; char p = cmd[i ++];
	while (!isdigit(p) and p != '-') p = cmd[i ++];
	bool f = p == '-' ? p = cmd[i ++] : 0;
	do n = n * 10 + (p ^ 48), p = cmd[i ++];
	while (isdigit(p) and cmd[i] != -1);
	i --;
	return f ? -n : n;
}

pan

额,作用是进行比较,返回合法与否,为了方便 whileihu 而生。

inline bool pan(int val1, int val2, int p) {
	switch (p) {
		case 1: return val1 == val2; break;
		case 2: return val1 != val2; break;
		case 3: return val1 < val2; break;
		case 4: return val1 > val2; break;
		case 5: return val1 <= val2; break;
		case 6: return val1 >= val2; break;
	}
}

get_r

效果是返回第一个没有匹配的 },作用是在 ihuwhilehor 里作为边界。

inline int get_r(uint ff) {
	int cl = 0;
	for(int i = ff; i < cmd.size(); i ++) {
		if(cmd[i] == '{') cl ++;
		if(cmd[i] == '}') {
			cl --;
			if(cl == -1) return i;
		}
	}
}

二级函数们

首先是算数的。

formula

得到一个公式的表达,并返回整个公式,非常有用

实现不算很难,主要是分辨符号和数字和变量。

注意边界条件,主要是由于它的几个不同使用条件。

inline Formula formula(uint & i) {
	vector<pair<string, int> > k;
	int fu = 1;
	while(cmd[i] != '\n' and cmd[i] != -1 and cmd[i] != ']' and cmd[i] != ',') {
		int j = i;
		string h = get_str(i);
		if(h == "+") fu = 1;
		else if(h == "-") fu = -1; // 得到符号
		else i = j; // 不是符号就退回
		pass(i);
		if(isdigit(cmd[i])) {
			int var = in(i); // 数字
			k.push_back({"",var * fu});
		}
		else {
			h = get_num(i); // 下面会讲这是啥
			k.push_back({h, fu});
		}
	}
	return k;
}

add

一个比较有用的函数,有用是因为 formula 有用。

效果为计算刚才得到的算式。

inline int add(vector<pair<string, int> > s) {
	int tmp = 0;
	for(pair<string, int> v : s) {
		if(v.first == "") {
			tmp += v.second;
			continue;
		}
		tmp += mp[v.first] * v.second;
	}
	return tmp;
}

顺便附上 calc,这是一个便于计算只使用一次的函数而生的。

inline int calc(uint &i) { return add(formula(i)); }

后面是其他的二级函数。

get_num

如果你仔细看了 formula 函数就会发现这个东西,它的作用是尝试读入一个合法的变量(并翻译),如果是数字就会直接返回而不会读入,也是很有用的函数,其中计算数组下标导致它和 formula 相互调用。

inline string get_num(uint &i) {
	pass(i);
	string tm = "";
	int aaa = 0;
	while(cmd[i] <= 'z' and cmd[i] >= 'a') tm += cmd[i ++];
	if(cmd[i] == '[') {
		i ++;
		aaa = calc(i);
		pass(i);
		i ++;
		return tm + '[' + to_string(aaa) + ']';
	} 
	return tm;
}

几个实现向的函数

没错!!!苦尽甘来!!!

有了前面的铺垫,接下来的函数都非常好实现了。

code_start

表示将程序位于 \([i,up]\) 的代码运行,作用是链接其他的函数和方便循环。

inline void code_start(uint &i, int up) {
	for(; i <= up;) {
		char op = cmd[i ++];
		if(op == '{') {
			while(op != 'v' and op != 'i' and op != 'h' and op != 'w') op = cmd[i ++];
			to__(i);
			if(op == 'v') var(i);
			if(op == 'i') ihu(i); 
			if(op == 'w') whil(i);
			if(op == 'h') hor(i);
		}
		if(op == ':') {
			op = cmd[i ++];
			if(op == 's') { // set
				i += 2;
				string t = get_num(i);
				i ++;
				int v = calc(i);
				mp[t] = v;
			}
			else {
				i += 5; // yosoro
				out(calc(i)); e_;
			}
		}
	}
}

var

美妙的定义,你发现这个根本不用实现。其实用一次 get_r 就行,不过给足尊重,写个函数。

inline void var(uint &i) {
	while(cmd[i] != '}') i ++;
}

change

求出前后两个变量然后更改就行,实现在 code_start 里面。

out

求出表达式的值然后输出,实现也在 code_start 里面。

ihu

首先求出符号类型,再得到两个表达式,比较并运行就行。

inline void ihu(uint &i) {
	int p; // 求符号
	string pp = get_str(i);
	switch(pp[0] + pp[1]) {
		case 'e' + 'q': p = 1; break;
		case 'n' + 'e': p = 2; break;
		case 'l' + 't': p = 3; break;
		case 'g' + 't': p = 4; break;
		case 'l' + 'e': p = 5; break;
		case 'g' + 'e': p = 6; break;
	} 
	i ++;
	pass(i);
	Formula fi = formula(i); // 得到两个表达式
	i ++;
	pass(i);
	Formula se = formula(i);
	int up = get_r(i);
	if(pan(add(fi), add(se), p)) {
		uint j = i + 1;
		code_start(j, up);
	}
	i = up;
}

whil

和上一个是不太一样的,注意到你得到的公式的表达是会改变的,所以公式要每次分别计算。

关于改变,这是一组样例。

{ vars
    a:int
    d:array[int, 0..22]
}
:set d[4], 10
{ while le, d[a], 9
    :set a, a+1
    :yosoro d[a]
}

如果不每次单独计算就会导致你一直用 le, d[0], 9 比较,然后程序就死了。

inline void whil(uint &i) {
	int p;
	string pp = get_str(i);
	switch(pp[0] + pp[1]) {
		case 'e' + 'q': p = 1; break;
		case 'n' + 'e': p = 2; break;
		case 'l' + 't': p = 3; break;
		case 'g' + 't': p = 4; break;
		case 'l' + 'e': p = 5; break;
		case 'g' + 'e': p = 6; break;
	} 
	i ++;
	pass(i);
	uint fi = i, i1 = i; 
	formula(i);
	i ++;
	pass(i);
	uint se = i, i2 = i;
	formula(i);
	int up = get_r(i);
	while(pan(calc(i1), calc(i2), p)) {
		uint j = i + 1;
		code_start(j, up);
		i1 = fi, i2 = se;
	}
	i = up;
}

hor

这个甚至还要好实现些,你连符号都不用读入了,直接读入一个变量两个表达式,然后 for 就完了,由于题目给了保证,甚至不用像 while 那样处理。

inline void hor(uint &i) {
	i ++;
	pass(i);
	string fi = get_num(i);
	i ++;
	pass(i);
	Formula se = formula(i);
	i ++;
	pass(i);
	Formula th = formula(i);
	int up = get_r(i);
	for(mp[fi] = add(se); pan(mp[fi], add(th), 5); mp[fi] ++) {
		uint j = i + 1;
		code_start(j, up);
	}
	mp[fi] --; // 题目里面对 hor 的定义和一般的 for 不太一样,这里要 --
	i = up;
}

然后呢?

你就 AC 啦!

示例代码

// code by 樓影沫瞬_Hz17
#include <bits/stdc++.h>
using namespace std;
 
#define getc() getchar_unlocked()
#define putc(a) putchar_unlocked(a)
#define en_ putc('\n')
#define e_ putc(' ')
 
using pii = pair<int, int>;
using Formula = vector<pair<string, int>>;
 
template<class T> inline void out(T n) {
	if(n < 0) putc('-'), n = -n;
	if(n > 9) out(n / 10);
	putc(n % 10 + '0');
}
 
const int N = 5e5 + 10;
 
string cmd;
string get_num(uint &i);
void code_start(uint &i, int up);
 
map<string, int> mp;
 
inline void to__(uint &i) {
	while(cmd[i] != ' ' and cmd[i] != -1 and cmd[i] != '\n') i ++;
}
 
inline void pass(uint &i) {
	while((cmd[i] == ' ' || cmd[i] == '\n'  || cmd[i] == '\t') and cmd[i] != -1) i ++; 
}
 
inline string get_str(uint &i) {
	string tmp = "";
	pass(i);
	if(cmd[i] == '+' || cmd[i] == '-') 
		return tmp = cmd[i ++];
	while(cmd[i] != ' ' and cmd[i] != ':' and cmd[i] != '\n' and cmd[i] != -1 and cmd[i] != ',' and cmd[i] != '+' and cmd[i] != '-') 
		tmp.push_back(cmd[i ++]);
	return tmp;
}
 
inline int in(uint &i) { 
	int n = 0; char p = cmd[i ++];
	while (!isdigit(p) and p != '-') p = cmd[i ++];
	bool f = p == '-' ? p = cmd[i ++] : 0;
	do n = n * 10 + (p ^ 48), p = cmd[i ++];
	while (isdigit(p) and cmd[i] != -1);
	i --;
	return f ? -n : n;
}
 
inline int add(Formula s) {
	int tmp = 0;
	for(pair<string, int> v : s) {
		if(v.first == "") {
			tmp += v.second;
			continue;
		}
		tmp += mp[v.first] * v.second;
	}
	return tmp;
}
 
inline Formula formula(uint & i) {
	Formula k;
	int fu = 1;
	while(cmd[i] != '\n' and cmd[i] != -1 and cmd[i] != ']' and cmd[i] != ',') {
		int j = i;
		string h = get_str(i);
		if(h == "+") fu = 1;
		else if(h == "-") fu = -1;
		else i = j;
		pass(i);
		if(isdigit(cmd[i])) {
			int var = in(i);
			k.push_back({"",var * fu});
		}
		else {
			h = get_num(i);
			k.push_back({h, fu});
		}
	}
	return k;
}
 
inline int calc(uint &i) { return add(formula(i)); }
 
inline string get_num(uint &i) {
	pass(i);
	string tm = "";
	int aaa = 0;
	while(cmd[i] <= 'z' and cmd[i] >= 'a') tm += cmd[i ++];
	if(cmd[i] == '[') {
		i ++;
		aaa = calc(i);
		pass(i);
		i ++;
		return tm + '[' + to_string(aaa) + ']';
	} 
	return tm;
}
 
inline int get_r(uint ff) {
	int cl = 0;
	for(int i = ff; i < cmd.size(); i ++) {
		if(cmd[i] == '{') cl ++;
		if(cmd[i] == '}') {
			cl --;
			if(cl == -1) return i;
		}
	}
}
 
inline bool pan(int val1, int val2, int p) {
	switch (p) {
		case 1: return val1 == val2; break;
		case 2: return val1 != val2; break;
		case 3: return val1 < val2; break;
		case 4: return val1 > val2; break;
		case 5: return val1 <= val2; break;
		case 6: return val1 >= val2; break;
	}
}
 
inline void var(uint &i) {
	while(cmd[i] != '}') i ++;
}
 
inline void ihu(uint &i) {
	int p;
	string pp = get_str(i);
	switch(pp[0] + pp[1]) {
		case 'e' + 'q': p = 1; break;
		case 'n' + 'e': p = 2; break;
		case 'l' + 't': p = 3; break;
		case 'g' + 't': p = 4; break;
		case 'l' + 'e': p = 5; break;
		case 'g' + 'e': p = 6; break;
	} 
	i ++;
	pass(i);
	Formula fi = formula(i);
	i ++;
	pass(i);
	Formula se = formula(i);
	int up = get_r(i);
	if(pan(add(fi), add(se), p)) {
		uint j = i + 1;
		code_start(j, up);
	}
	i = up;
}
 
inline void whil(uint &i) {
	int p;
	string pp = get_str(i);
	switch(pp[0] + pp[1]) {
		case 'e' + 'q': p = 1; break;
		case 'n' + 'e': p = 2; break;
		case 'l' + 't': p = 3; break;
		case 'g' + 't': p = 4; break;
		case 'l' + 'e': p = 5; break;
		case 'g' + 'e': p = 6; break;
	} 
	i ++;
	pass(i);
	uint fi = i, i1 = i; 
	formula(i);
	i ++;
	pass(i);
	uint se = i, i2 = i;
	formula(i);
	int up = get_r(i);
	while(pan(calc(i1), calc(i2), p)) {
		uint j = i + 1;
		code_start(j, up);
		i1 = fi, i2 = se;
	}
	i = up;
}
 
inline void hor(uint &i) {
	i ++;
	pass(i);
	string fi = get_num(i);
	i ++;
	pass(i);
	Formula se = formula(i);
	i ++;
	pass(i);
	Formula th = formula(i);
	int up = get_r(i);
	for(mp[fi] = add(se); pan(mp[fi], add(th), 5); mp[fi] ++) {
		uint j = i + 1;
		code_start(j, up);
	}
	mp[fi] --;
	i = up;
}
 
inline void code_start(uint &i, int up) {
	for(; i <= up;) {
		char op = cmd[i ++];
		if(op == '{') {
			while(op != 'v' and op != 'i' and op != 'h' and op != 'w') op = cmd[i ++];
			to__(i);
			if(op == 'v') var(i);
			if(op == 'i') ihu(i); 
			if(op == 'w') whil(i);
			if(op == 'h') hor(i);
		}
		if(op == ':') {
			op = cmd[i ++];
			if(op == 's') {
				i += 2;
				string t = get_num(i);
				i ++;
				int v = calc(i);
				mp[t] = v;
			}
			else {
				i += 5;
				out(calc(i)); e_;
			}
		}
	}
}
 
signed main() {
	#ifndef ONLINE_JUDGE
		freopen("i.ru", "r", stdin);
		freopen("o", "w", stdout);
	#endif
	char c;
	while(1) {
		c = getc();
		if(c == '#') {
			while(c != '\n') c = getc();
			cmd += c;
		}
		else cmd.push_back(c);
		if(c == -1) break;
	}
	// cout << cmd;
	uint i = 0;
	code_start(i, cmd.size() - 1);
}	
// 星間~ 干渉~ 融解~ 輪迴~ 邂逅~ 再生~ ララバイ~

后附

A + B problem 的主函数,实测可过。

signed main() {
	int a, b;
	cin >> a >> b;
	cmd =   (string)
			"{ vars\n" +
			"	a:int\n" +
			"	b:int\n" +
			"}\n" +
			":set a, "+ to_string(a) +"\n" +
			":set b, "+ to_string(b) +"\n" +
			":yosoro a+b\n"
		;
	cmd.push_back(-1);
	uint i = 0;
	code_start(i, cmd.size() - 1);
}	
posted @ 2025-09-19 22:04  樓影沫瞬_17Hz  阅读(13)  评论(0)    收藏  举报