解题记录说是 | P3695 CYaRon!语
link
起因
闲的没事找模拟做,发现这个部分分的档次很多,而且好想挺好做,就做了。
是分着部分分做的,而且完全就是那种苦(封装各种实现)尽甘(飞速写完 ihu
、hor
、while
)来的感觉,很爽的。
讲解
首先我们显然发现注释是没有用的,所以略过注释,并且发现我们先读入所有的东西再处理会让ihu
、hor
、while
等变得更好处理,所以我们一次性读到 \(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
额,作用是进行比较,返回合法与否,为了方便 while
和 ihu
而生。
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
效果是返回第一个没有匹配的 }
,作用是在 ihu
、while
、hor
里作为边界。
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);
}