NOIp2017 D1T2

前情提要

夏令营教模拟,老师给了我们这个作业。我当时特喜欢刷水题。当时,我觉得这道题很水,轻松搞定,于是直奔满分而去。结果......也就调了亿会

为了弥补这一个月之劳累,特写此题解。

铺垫

(注:这里我用字符串存储小明认为的复杂度。)

定义该循环“对复杂度有贡献”当且仅当循环下界与上界之差远大于 \(100\)。这种循环是我们要统计的。

定义“废循环”为不对复杂度的次数有贡献的循环。这种循环是需要避免计算入复杂度的。

处理语法错误的方法

一个程序语法错误当且仅当:(括号内为解决方案)

  • 变量重名;(用 \(arr\) 数组记录变量是否被使用)
  • FE 不对应。(用栈模拟循环)

正片

本题解着重讲解模拟思路,想倒不难。

处理复杂度步骤描述

首先:读入每行程序的第一个字符。

F:处理此循环的标记

  • 读入变量名、循环下界和上界。
  • 如果和 \(arr\) 数组之前记录的变量名重名:
    • 标记语法错误,重新读入,忽略该组程序。(避免重复输出)
    • 否则:
      • \(arr\) 数组中标记变量名,并将变量名压入循环栈中;(方便销毁变量名)
      • n 赋值为一个极大值;(方便计算复杂度)
      • 如果这个循环没有被标记为废循环,且循环对复杂度有贡献:
        • 将该循环标记为有贡献的;
        • 当前次数 \(w\to w+1\)
        • 更新最大次数 \(res\)
      • 否则:
        • 标记该变量名下的循环为废循环。
if(a == 'F'){
    cin >> b >> c >> d;
    if(arr[b - 'a'] && !ERR){
        cout << "ERR\n";
        ERR = 1; 
        continue; //变量名重复
    } else {
        arr[b - 'a'] = 1;
        st.push(b); 
        if(c == "n"){ c = "20000";}
        if(d == "n"){ d = "20000";}
        int C = stoi(c), D = stoi(d);
        if(D-C > 1000 && flag == -1){
            eff[b - 'a'] = 1; ++w;
            res = max(w, res);//判定复杂度
        } else if(C > D){
            flag = b; //废循环
        }
    }
}

E:撤销之前对与之对应的循环的标记

  • 如果这个栈是空的:
    • 标记语法错误,重新读入,忽略该组程序。
  • 否则:
    • 取出栈顶的变量名;
    • 从栈中弹出这个循环;
    • 撤销变量名重名标记;
    • 如果该变量名下的循环为废循环:
      • 撤销废循环标记。
    • 否则,如果该变量名下的循环对复杂度有贡献:
      • 撤销贡献标记,当前次数 \(w\to w-1\)
else {
    if(st.empty()){
        if(! ERR){
             cout << "ERR\n"; 
             ERR = 1;
        } continue;
    } 
    int cur = st.top();
    st.pop();
    arr[cur - 'a'] = 0;
    if(flag == cur) { flag = -1; }
    if(eff[cur - 'a']) {
        eff[cur - 'a'] = 0;
        w --;
    }
}

收尾,判定复杂度

  • 如果栈中还有元素:
    • 清空栈;
    • 如果没有输出过 ERR
      • 报告语法错误。
    • 读入下一组数据。
  • 特判 O(1),即 \(\Theta(n^0)\)
    • 如果语法正确且最终次数 \(res\)\(0\)
      • 输出 Yes
    • 否则:
      • 输出 No
  • 否则截取复杂度的次数部分。\(^{[1]}\)(将截取的字符串转换为 int 类型)
  • 如果次数部分等于 \(res\)
    • 输出 Yes
  • 否则:
    • 输出 No

\([1]\):这类格式以 O(n^45) 为例。前 \(4\) 个字符为固定的,不为数字;最后一个字符为 )。所以,截取数字要从第 \(5\) 个字符开始(即 s[4]4)到倒数第二个字符(s[s.size() - 1]5),即可截取到 45

if(st.size()){
    while(st.size()){
        st.pop();
    }
    if(!ERR) {
        cout << "ERR\n";
    }
    continue;
}
if(s == "O(1)" && !ERR){
    if(res == 0) {
        cout << "Yes\n";
    } else {
        cout << "No\n";
    }
} else if(!ERR) {
    string s1;
    for(int i=4; i<s.size()-1; ++i){
        s1 += s.substr(i, 1);
    }
    if(stoi(s1) == res) {
        cout << "Yes\n";
    } else {
        cout << "No\n";
    }
}

坑总结

  1. 多测必清空!
  2. 想好思路再写代码;
  3. 把语法错误的循环完全忽略。记录是否属于这类循环,并尽量在所有判断的条件加上 ! ERR
  4. 特判循环下界和上界都是 n 的情况;
  5. 小明判定的复杂度中,次数可能不止一位。

总代码

模拟题最重要的是细节和思维。想到了思路,你完全可以敲出正解代码。此处建议自行编写。

#include <bits/stdc++.h>
using namespace std;
int _;
bool arr[30], eff[30];

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    stack<int> st;
    cin >> _;
    while(_ --){
        int n, w=0, res=0, flag = -1, ERR = 0;
        string s, c, d;
        char a, b;
        memset(arr, 0, sizeof arr);
        memset(eff, 0, sizeof eff);
        cin >> n >> s;
        while(n --){
            cin >> a;
            if(a == 'F'){
                cin >> b >> c >> d;
                if(arr[b - 'a'] && !ERR){
                    cout << "ERR\n";
                    ERR = 1; 
                    continue; //变量名重复
                } else {
                    arr[b - 'a'] = 1;
                    st.push(b); 
                    if(c == "n"){ c = "20000";}
                    if(d == "n"){ d = "20000";}
                    int C = stoi(c), D = stoi(d);
                    if(D-C > 1000 && flag == -1){
                        eff[b - 'a'] = 1; ++w;
                        res = max(w, res);//判定复杂度
                    } else if(C > D){
                        flag = b; //废循环
                    }
                }
            } else {
                if(st.empty()){
                    if(! ERR){
                        cout << "ERR\n"; 
                        ERR = 1;
                    } continue;
                } 
                int cur = st.top();
                st.pop();
                arr[cur - 'a'] = 0;
                if(flag == cur) { flag = -1; }
                if(eff[cur - 'a']) {
                    eff[cur - 'a'] = 0;
                    w --;
                }
            }
        }
        if(st.size()){
            while(st.size()){
                st.pop();
            }
            if(!ERR) {
                cout << "ERR\n";
            }
            continue;
        }
        if(s == "O(1)" && !ERR){
            if(res == 0) {
                cout << "Yes\n";
            } else {
                cout << "No\n";
            }
        } else if(!ERR){
            string s1;
            for(int i=4; i<s.size()-1; ++i){
                s1 += s.substr(i, 1);
            }
            if(stoi(s1) == res) {
                cout << "Yes\n";
            } else {
                cout << "No\n";
            }
        }
    }
    return 0;
}
posted @ 2025-07-25 10:45  RedAccepted  阅读(11)  评论(0)    收藏  举报