CYaRon!语

· · 题解

博客园

起因

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

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

讲解

首先我们显然发现注释是没有用的,所以略过注释,并且发现我们先读入所有的东西再处理会让 ihuhorwhile 等变得更好处理,所以我们定义一个 cmd,一次性读到 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) {
    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});
        }11246 字符 
        else {
            h = get_num(i); // 下面会讲这是啥
            k.push_back({h, fu});
        }
    }
    return k;
}

add

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

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

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;
}

顺便附上 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);
}