1 条题解

  • 0
    @ 2025-12-16 10:50:53

    你好!作为一名 OI 教练,我非常理解你需要一套高质量、覆盖全面的测试数据来验证学生的代码。

    为了方便你直接使用,我编写了一个 “一键生成工厂” 的 C++ 程序。这个程序运行后,会在当前目录下自动生成 1.in ~ 10.in 以及对应的标准答案 1.out ~ 10.out

    核心设计思路

    1. 覆盖度
      • 小数据 (1-3):方便手动计算验证,包含正数、负数混合。
      • 边界情况 (4-6)
        • 全零/倍数序列:测试 ans 是否会爆 int(答案最大为 N(N+1)/25×109N(N+1)/2 \approx 5 \times 10^9,必须用 long long)。
        • 极小 KK=2K=2,测试奇偶性同余。
        • 极大 KKNK \approx N
      • 大数据 (7-10)N=105N=10^5,全随机,正负混合,测试时间复杂度 O(N)O(N) 是否达标。
    2. 安全性:使用了 C++11 的 mt19937 随机数生成器,比 rand() 质量更高且分布更均匀。
    3. 自动化:生成输入文件后,立即调用内置的标准解答函数生成输出文件,确保数据和答案绝对匹配。

    C++ 数据生成器代码 (Generator + Solver)

    请将以下代码保存为 gen_data.cpp,编译运行即可。

    /**
     * 题目: 序列整除 (Subarray Divisibility) 数据生成器
     * 功能: 自动生成 1.in ~ 10.in 及对应的 1.out ~ 10.out
     * 作者: OI 教练
     * 
     * 包含两个部分:
     * 1. Generator: 根据预设策略生成测试数据
     * 2. Solver: 读取生成的 .in 文件,算出标准答案写入 .out 文件
     */
    
    #include <bits/stdc++.h>
    using namespace std;
    
    // ================= 标准解答部分 (Solver) =================
    // 对应之前教案中的标准解法,用于生成 .out 文件
    namespace Solver {
        const int MAXK = 10005;
        int cnt[MAXK]; 
    
        void solve(string intput_file, string output_file) {
            ifstream fin(intput_file);
            ofstream fout(output_file);
            
            if (!fin.is_open() || !fout.is_open()) {
                cerr << "Error opening files: " << intput_file << endl;
                return;
            }
    
            int n, k;
            fin >> n >> k;
            
            // 初始化
            memset(cnt, 0, sizeof(cnt));
            cnt[0] = 1; // 0 是初始前缀和
    
            long long ans = 0;
            int current_sum = 0;
            
            for (int i = 0; i < n; ++i) {
                int val;
                fin >> val;
                current_sum += val;
                
                // 核心公式:处理负数取模
                int remainder = (current_sum % k + k) % k;
                
                ans += cnt[remainder];
                cnt[remainder]++;
            }
            
            fout << ans << endl;
            
            fin.close();
            fout.close();
            cout << "Generated: " << output_file << " (Ans: " << ans << ")" << endl;
        }
    }
    
    // ================= 数据生成部分 (Generator) =================
    namespace Generator {
        mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
    
        // 生成范围 [min, max] 的随机整数
        int random_int(int min_val, int max_val) {
            uniform_int_distribution<int> dist(min_val, max_val);
            return dist(rng);
        }
    
        // 生成数据的主函数
        // id: 测试点编号
        // n: 序列长度
        // k: 除数
        // min_val, max_val: 数组元素的值域
        // type: 特殊构造类型 (0:随机, 1:全是K的倍数, 2:含大量0)
        void make_data(int id, int n, int k, int min_val, int max_val, int type) {
            string in_file = to_string(id) + ".in";
            string out_file = to_string(id) + ".out";
            
            ofstream fout(in_file);
            
            fout << n << " " << k << "\n";
            
            for (int i = 0; i < n; ++i) {
                int val;
                if (type == 0) {
                    // 普通随机
                    val = random_int(min_val, max_val);
                } else if (type == 1) {
                    // 特殊构造:全是 K 的倍数
                    // 这种情况下任意子段和都能整除 K,答案应为 N*(N+1)/2
                    // 用于测试答案是否用了 long long
                    int mul = random_int(min_val / k, max_val / k);
                    val = mul * k;
                } else if (type == 2) {
                    // 特殊构造:大量 0 (80% 概率为0)
                    if (random_int(1, 100) <= 80) val = 0;
                    else val = random_int(min_val, max_val);
                }
                
                fout << val << (i == n - 1 ? "" : " ");
            }
            fout << "\n";
            fout.close();
            
            // 生成完输入后,立即调用 Solver 生成输出
            Solver::solve(in_file, out_file);
        }
    }
    
    int main() {
        // 加速
        ios::sync_with_stdio(false);
        
        cout << "--- Starting Data Generation ---" << endl;
    
        // 测试点 1-3: 小规模数据,方便调试,包含负数
        Generator::make_data(1, 10, 5, 0, 10, 0);       // 纯正数小数据
        Generator::make_data(2, 50, 7, -10, 10, 0);     // 混合正负小数据
        Generator::make_data(3, 100, 13, -100, 100, 0); // 稍大的小数据
    
        // 测试点 4-6: 边界与特殊情况
        // Point 4: 大量 0 (0 被任何数整除,测试 cnt[0] 处理)
        Generator::make_data(4, 1000, 50, -100, 100, 2); 
        
        // Point 5: 全是 K 的倍数 (压力测试 long long 溢出)
        // N=1000, Ans ≈ 5*10^5,稍微大一点
        Generator::make_data(5, 5000, 10, -1000, 1000, 1);
        
        // Point 6: K 极小 (K=2),测试奇偶性
        Generator::make_data(6, 10000, 2, -10000, 10000, 0);
    
        // 测试点 7-10: 大规模随机数据 (10^5 级别)
        // Point 7: N 大,K 中等,正数
        Generator::make_data(7, 100000, 233, 1, 10000, 0);
        
        // Point 8: N 大,K 大,负数多
        Generator::make_data(8, 100000, 9999, -10000, -1, 0);
        
        // Point 9: N 最大,K 中等,完全随机 (-10000 ~ 10000)
        Generator::make_data(9, 100000, 5000, -10000, 10000, 0);
        
        // Point 10: 极限数据 N=10^5, K=10^4
        Generator::make_data(10, 100000, 10000, -10000, 10000, 0);
    
        cout << "--- All 10 test cases generated successfully! ---" << endl;
        
        return 0;
    }
    

    测试点详细设计说明

    测试点 NN KK 数据特征 考察目的
    1 10 5 小范围正数 基础逻辑验证,方便手算核对
    2 50 7 含少量负数 验证 (sum%k + k)%k 的初步正确性
    3 100 13 较多负数 进一步验证负数同余处理
    4 1000 50 大量 0 0 对前缀和无影响,测试逻辑是否被大量相同前缀和卡住
    5 5000 10 全为 K 的倍数 答案为最大值 N(N+1)/2N(N+1)/2必测 long long 是否溢出
    6 10000 2 K=2K=2 (奇偶) 只有余数 0 和 1 两个桶,测试桶的高频冲突
    7 10510^5 233 纯正数,大 N 测试无负数情况下的时间复杂度
    8 9999 纯负数,大 N 测试全负数前缀和递减情况下的正确性
    9 5000 强随机,正负混合 模拟真实竞赛数据的平均分布情况
    10 10410^4 极限数据 题目给定范围的上限测试,验证 1.0s 内通过

    使用方法

    1. 将代码保存为 gen.cpp
    2. 编译:g++ gen.cpp -o gen -O2 (推荐开启 O2 优化,虽然生成器通常不需要,但是个好习惯)。
    3. 运行:
      • Windows: gen.exe
      • Linux/Mac: ./gen
    4. 运行结束后,你会发现当前文件夹下多了 1.in~10.in1.out~10.out 共20个文件。
    5. 将这些文件上传到你的 OJ 后台即可。

    这个生成器不仅生成数据,还自带了我们在教案中讨论过的“标准解答”来产生输出,保证了数据的一致性。希望能帮助你顺利完成题目配置!

    • 1

    信息

    ID
    19329
    时间
    1000ms
    内存
    32MiB
    难度
    10
    标签
    (无)
    递交数
    1
    已通过
    1
    上传者