第十四届蓝桥杯大赛软件赛省赛(C/C++ 大学C组)

小明 2025-04-29 08:08:54 3

蓝���杯 2023年省赛真题 C/C++ 大学C组

  • 试题 A: 求和
  • 试题 B: 工作时长
  • 试题 C: 三国游
  • 试题 D: 填充
  • 试题 E: 翻转
  • 试题 F: 子矩阵
  • 试题 G: 互质数的个数
  • 试题 H: 异或和之差
  • 试题  I: 公因数匹配
  • 试题 J: 子树的大小

    试题 A: 求和

    本题总分: 5 5 5 分


    【问题描述】

      求 1 1 1 (含)至 20230408 20230408 20230408 (含)中每个数的和。

    【答案提交】

      这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。


    204634714038436


    自然数列求和, 1 + 2 + ⋯ + n = n ( n + 1 ) 2 1+2+\cdots+n=\cfrac {n(n+1)}2 1+2+⋯+n=2n(n+1)​。

    #include 
    int main() {
        printf("%lld", 20230408 * (20230408 + 1ll) / 2);
    }
    

    或者迭代答案。

    #include 
    int main() {
        long long ans = 0;
        for (int i = 1; i 
        freopen("lock-in.all.txt", "r", stdin);
        std::priority_queue 
            
           
             Y 
            
           
             + 
            
           
             Z 
            
           
          
            X > Y + Z 
           
          
        X>Y+Z 时,我们认为魏国获胜。小蓝想知道游戏结束时如果有其中一个国家获胜,最多发生了多少个事件?

      如果不存在任何能让某国获胜的情况,请输出 − 1 −1 −

    【输入格式】

      输入的第一行包含一个整数 n n n。

      第二行包含 n n n 个整数表示 A i A_i Ai​,相邻整数之间使用一个空格分隔。

      第三行包含 n n n 个整数表示 B i B_i Bi​,相邻整数之间使用一个空格分隔。

      第四行包含 n n n 个整数表示 C i C_i Ci​,相邻整数之间使用一个空格分隔。

    【输出格式】

      输出一行包含一个整数表示答案。

    【样例输入】

    3
    1 2 2
    2 3 2
    1 0 7
    

    【样例输出】

    2
    

    【样例说明】

      发生两个事件时,有两种不同的情况会出现获胜方。

      发生 1 , 2 1, 2 1,2 事件时蜀国获胜。

      发生 1 , 3 1, 3 1,3 事件时吴国获胜。

    【评测用例规模与约定】

      对于 40 % 40\% 40% 的评测用例, n ≤ 500 n ≤ 500 n≤500;

      对于 70 % 70\% 70% 的评测用例, n ≤ 5000 n ≤ 5000 n≤5000;

      对于所有评测用例, 1 ≤ n ≤ 1 0 5 , 1 ≤ A i , B i , C i ≤ 1 0 9 1 ≤ n ≤ 10^5,1 ≤ A_i, B_i,C_i ≤ 10^9 1≤n≤105,1≤Ai​,Bi​,Ci​≤


    贪心


    同时考虑三个国家是相当困难的,于是考虑分别求出魏蜀吴分别获胜的话最大事件数,最优答案就是这若干个数中的最大值。

    以魏举例,记第 i i i 个事件对魏获胜的贡献为 x i − y i − z i x_i -y_i -z_i xi​−yi​−zi​,按贡献降序重排事件,找到一个 k k k,满足 ∑ i = 1 k x i > ∑ i = 1 k ( y i + z i ) \sum_{i=1}^kx_i >\sum_{i=1}^k(y_i + z_i) ∑i=1k​xi​>∑i=1k​(yi​+zi​) 且 k k k 尽可能大,若 k b.x - b.y - b.z; }, [](long long x, long long y, long long z) -> bool { return x > y + z; }), std::max( greedy([](tuple a, tuple b)-> bool { return a.y - a.x - a.z > b.y - b.x - b.z; }, [](long long x, long long y, long long z) -> bool { return y > x + z; }), greedy([](tuple a, tuple b)-> bool { return a.z - a.x - a.y > b.z - b.x - b.y; }, [](long long x, long long y, long long z) -> bool { return z > x + y; }) ))); }

    22岁,喜欢屎山代码。


    试题 D: 填充

    时间限制: 1.0 s 1.0\mathrm s 1.0s 内存限制: 256.0 M B 256.0\mathrm{MB} 256.0MB 本题总分: 10 10 10 分


    【问题描述】

      有一个长度为 n n n 的 01 01 01 串,其中有一些位置标记为 ? ? ?,这些位置上可以任意填充 0 0 0 或者 1 1 1,请问如何填充这些位置使得这个 01 01 01 串中出现互不重叠的 00 00 00 和 11 11 11 子串最多,输出子串个数。

    【输入格式】

      输入一行包含一个字符串。

    【输出格式】

      输出一行包含一个整数表示答案。

    【样例输入】

    1110?0
    

    【样例输出】

    2
    

    【样例说明】

      如果在问号处填 0 0 0,则最多出现一个 00 00 00 和一个 11 : 111000 11:111000 11:

    【评测用例规模与约定】

      对于所有评测用例, 1 ≤ n ≤ 1000000 1 ≤ n ≤ 1000000 1≤n≤


    贪心


    如果以01相接的地方为断点,将01串拆分开来,如1110?0拆分成111、0?0,则显然第二段中的问号应填0,于是只需考虑若干问号存在01之间的情况,如果若干问号的左侧段长为奇数,则考虑将一个问号变为左侧段对应的值,使总答案加一,然后将所有问号变为右侧段对应的值,若剩余问号为奇数则这个数字除二向下取整是雷打不动的,而多余的一个变为左侧对答案无贡献,所以直接变右,这么模拟着递推过来就行。

    实现时连续的问号同时当做0、1来看待,然后找到子串后清空累计即可。

    #include 
    int ans, cnt['1' + 1];
    char buf[1000010];
    int main() {
        gets(buf);
        for (int i = 0; buf[i]; ++i) {
            if (buf[i] == '?') {
                if (cnt['0']) ++cnt['0'];
                else if (cnt['1']) ++cnt['1'];
                else ++cnt['0'], ++cnt['1'];
            } else ++cnt[buf[i]], cnt[buf[i] ^ 1] = 0;
            if (cnt['0'] == 2 || cnt['1'] == 2)
                ++ans, cnt['0'] = cnt['1'] = 0;
        }
        printf("%d", ans);
    }
    

    好像又把代码写成一坨了


    试题 E: 翻转

    时间限制: 1.0 s 1.0\mathrm s 1.0s 内存限制: 256.0 M B 256.0\mathrm{MB} 256.0MB 本题总分: 15 15 15 分


    【问题描述】

      小蓝用黑白棋的 n n n 个棋子排成了一行,他在脑海里想象出了一个长度为 n n n 的 01 01 01 串 T T T,他发现如果把黑棋当做 1 1 1,白棋当做 0 0 0,这一行棋子也是一个长度为 n n n 的 01 01 01 串 S S S。

      小蓝决定,如果在 S S S 中发现一个棋子和它两边的棋子都不一样,就可以将其翻转变成另一个颜色。也就是说,如果 S S S 中存在子串 101 101 101 或者 010 010 010,就可以选择将其分别变为 111 111 111 和 000 000 000,这样的操作可以无限重复。

      小蓝想知道最少翻转多少次可以把 S S S 变成和 T T T 一模一样。

    【输入格式】

      输入包含多组数据。

      输入的第一行包含一个正整数 D D D 表示数据组数。

      后面 2 D 2D 2D 行每行包含一个 01 01 01 串,每两行为一组数据,第 2 i − 1 2i − 1 2i−1 行为第 i i i 组数据的 T i T_i Ti​,第 2 i 2i 2i 行为第 i i i 组数据的 S i S_i Si​, S i S_i Si​ 和 T i T_i Ti​ 长度均为 n i n_i ni​。

    【输出格式】

      对于每组数据,输出一行包含一个整数,表示答案,如果答案不存在请输出 −

    【样例输入】

    2
    1000111
    1010101
    01000
    11000
    

    【样例输出】

    2
    -1
    

    【评测用例规模与约定】

      对于 20 % 20\% 20% 的评测用例, 1 ≤ ∑ 1 D n i ≤ 10 1 ≤\sum_1^{{}_D} n_i ≤ 10 1≤∑1D​​ni​≤10;

      对于所有评测用例,保证 1 ≤ ∑ 1 D n i ≤ 1 0 6 , n i > 0 1 ≤\sum_1^{{}_D} n_i ≤ 10^6 ,n_i > 0 1≤∑1D​​ni​≤106,ni​>0。


    由题意可知,端点棋子是无法翻转的,而当某一个棋子翻转时,它与相邻棋子连续相同,故无法产生新的翻转点,因此端点特判然后遍历模拟就行。

    #include 
    char T[1000010], S[1000010];
    int D, cnt;
    int main() {
        for (scanf("%d\n", &D); gets(T + 1), gets(S + 1), D--;) {
            for (int i = 1; S[i]; ++i) {
                if (S[i] == T[i]) continue;
                if (S[i - 1] == S[i] ^ 1 && S[i - 1] == S[i + 1]) S[i] ^= 1, ++cnt;
                else {
                    cnt = -1;
                    break;
                }
            }
            printf("%d\n", cnt), cnt = 0;
        }
    }
    

    试题 F: 子矩阵

    时间限制: 2.0 s 2.0\mathrm s 2.0s 内存限制: 256.0 M B 256.0\mathrm{MB} 256.0MB 本题总分: 15 15 15 分


    【问题描述】

      给定一个 n × m n × m n×m ( n n n 行 m m m 列)的矩阵。

      设一个矩阵的价值为其所有数中的最大值和最小值的乘积。求给定矩阵的所有大小为 a × b a × b a×b ( a a a 行 b b b 列)的子矩阵的价值的和。

      答案可能很大,你只需要输出答案对 998244353 998244353 998244353 取模后的结果。

    【输入格式】

      输入的第一行包含四个整数分别表示 n , m , a , b n, m, a, b n,m,a,b,相邻整数之间使用一个空格分隔。

      接下来 n n n 行每行包含 m m m 个整数,相邻整数之间使用一个空格分隔,表示矩阵中的每个数 A i , j A_{i, j} Ai,j​。

    【输出格式】

      输出一行包含一个整数表示答案。

    【样例输入】

    2 3 1 2
    1 2 3
    4 5 6
    

    【样例输出】

    58
    

    【样例说明】

       1 × 2 + 2 × 3 + 4 × 5 + 5 × 6 = 58 1 × 2 + 2 × 3 + 4 × 5 + 5 × 6 = 58 1×2+2×3+4×5+5×6=

    【评测用例规模与约定】

      对于 40 % 40\% 40% 的评测用例, 1 ≤ n , m ≤ 100 1 ≤ n, m ≤ 100 1≤n,m≤100;

      对于 70 % 70\% 70% 的评测用例, 1 ≤ n , m ≤ 500 1 ≤ n, m ≤ 500 1≤n,m≤500;

      对于所有评测用例, 1 ≤ a ≤ n ≤ 1000   1 ≤ b ≤ m ≤ 1000   1 ≤ A i , j ≤ 1 0 9 1 ≤ a ≤ n ≤ 1000\ 1 ≤ b ≤ m ≤ 1000\ 1 ≤ A_{i, j} ≤ 10^9 1≤a≤n≤1000 1≤b≤m≤1000 1≤Ai,j​≤


    通过单调队列,先将矩阵(i-a+1,j)(i,j)的最值求出,然后如法炮制求出(i-a+1,j-b+1)(i,j-b+1)、(i-a+1,j-b+2)(i,j-b+2)、…、(i-a+1,j)(i,j)的最值,即(i-a+1,j-b+1)(i,j)的最值。

    单调队列求最值懒得讲了,关键字:单调队列、滑动窗口、区间最值,自己搜着看吧。

    #include 
    int n, m, a, b, maxh, maxt, minh, mint, max[1000][1000], min[1000][1000], maxq[1010], minq[1010];
    int main() {
        scanf("%d %d %d %d", &n, &m, &a, &b);
        for (int i = 0; i  i) ++minh;
                min[i][j] = max[minq[minh]][j];
                max[i][j] = max[maxq[maxh]][j];
                if (i - a >= 0) {
                    while (maxh != maxt && max[i - a][j] >= max[maxq[maxt - 1]][j]) --maxt;
                    while (minh != mint && max[i - a][j] 
            maxh = maxt = minh = mint = 0;
            for (int j = 0; j = 1) {
            if (b & 1) p = p * a % MOD;
            a = a * a % MOD;
        }
        return p;
    }
    long long a, b, ans;
    int main() {
        scanf("%d %lld", &a, &b);
        ans = qpow(a, b);
        for (int p = 2; p * p 
            if (a % p == 0) {
                ans = ((ans * qpow(p, MOD - 2)) % MOD) * (p - 1) % MOD;
                while (a % p == 0) a /= p;
            }
        }
        if (a  1) ans = ((ans * qpow(a, MOD - 2)) % MOD) * (a - 1) % MOD;
        printf("%lld", ans);
    }
    

    截止2023年5月12日16:31:11,dotcpp上的民间数据有误,当 a a a取 a b a^b ab余数部分,并且 a n s ∤   p ans\not|\ p ans∣ p时才能通过所有用例,错的离谱,害得我de半天bug。


    试题 H: 异或和之差

    时间限制: 1.0 s 1.0\mathrm s 1.0s 内存限制: 256.0 M B 256.0\mathrm{MB} 256.0MB 本题总分: 20 20 20 分


    【问题描述】

      给定一个含有 n n n 个元素的数组 A i A_i Ai​,你可以选择两个不相交的子段。求出这两个子段内的数的异或和的差值的最大值。

    【输入格式】

      输入的第一行包含一个整数 n n n 。

      第二行包含 n n n 个整数 A i A_i Ai​,相邻整数之间使用一个空格分隔。

    【输出格式】

      输出一行包含一个整数表示答案。

    【样例输入】

    6
    1 2 4 9 2 7
    

    【样例输出】

    14
    

    【样例说明】

      两个子段可以分别选 1 1 1 和 4 , 9 , 2 4,9,2 4,9,2,差值为 15 − 1 = 14 15 − 1 = 14 15−1=

    【评测用例规模与约定】

      对于 40 % 40\% 40% 的评测用例, n ≤ 5000 n ≤ 5000 n≤5000;

      对于所有评测用例, 2 ≤ n ≤ 2 × 1 0 5 , 0 ≤ A i ≤ 2 20 2 ≤ n ≤ 2 × 10^5,0 ≤ A_i ≤ 2^{20} 2≤n≤2×105,0≤Ai​≤


    01字典树,里面存前缀异或和,然后查询[1,i][i,n]的最值,做个dp最后枚举答案。

    #include 
    #include 
    int n, m = 20, ans, cur, a[200010], lmax, lmin, rmax[200010], rmin[200010], trie[800010][2];
    void add(int x) {
        int rt = 0;
        for (int i = m; ~i; --i) {
            int b = (x >> i) & 1;
            if (!trie[rt][b]) trie[rt][b] = ++cur;
            rt = trie[rt][b];
        }
    }
    int xor_max(int x) {
        int rt = 0, max = 0;
        for (int i = m; ~i; --i) {
            int b = (x >> i) & 1;
            if (trie[rt][b ^ 1]) {
                rt = trie[rt][b ^ 1];
                max |= 1 
        int rt = 0, min = 0;
        for (int i = m; ~i; --i) {
            int b = (x > i) & 1;
            if (trie[rt][b]) rt = trie[rt][b];
            else {
                rt = trie[rt][b ^ 1];
                min |= 1 
        scanf("%d", &n);
        for (int i = 1; i 
            add(s), s ^= a[i];
            rmax[i] = std::max(rmax[i + 1], xor_max(s));
            rmin[i] = std::min(rmin[i + 1], xor_min(s));
        }
        for (; cur; --cur) trie[cur][0] = trie[cur][1] = 0;
        trie[0][1] = trie[0][0] = 0;
        for (int i = 1, s = 0; i 
The End
微信