CSP2025年

J组:
第一题:拼数 (number)
思路分析
题目要求使用一个字符串中包含的数字字符拼成一个最大的正整数。

要组成最大的数,我们的策略应该是把越大的数字放在越高的位(即越靠前的位置)。例如,用数字
能拼成的最大数就是

因此,解题思路非常直接:

从输入的字符串中提取出所有的数字字符。
将这些数字字符作为一个字符集合,进行降序排列(‘9’ > ‘8’ > … > ‘0’)。
将排序后的数字字符依次输出,拼接成最终的字符串,即为所求的最大正整数。

第二题:座位 (seat)
思路分析
题目要求根据考生的成绩确定其在一个

列考场中的“蛇形”座位。座位排布规则为:

第1, 3, 5, … (奇数)列,从上到下排(行号 1 -> n)。
第2 4, 6, … (偶数)列,从下到上排(行号 n -> 1)。
解题步骤如下:

读入
,
和全部
个考生的成绩。根据题目和样例,输入的第一个成绩
就是我们要找的目标考生的成绩。
将所有成绩进行降序排序,以确定每个成绩的排名。
在排序后的列表中找到目标考生成绩的位置。这个位置就是他的排名(从0开始计数),我们称之为

根据排名
计算座位的行列号(均从0开始计数):
考生所在的列号

考生在该列中的位置(从该列的起始方向数起)

根据列号的奇偶性,结合
计算最终的行号:
如果
是偶数(对应第1, 3…列),方向是从上到下,所以行号

如果
是奇数(对应第2, 4…列),方向是从下到上,所以行号

最后,将从0开始的行列号
,
分别加1,得到题目要求的从1开始的行列号并输出。

代码:

#include <bits/stdc++.h>

using namespace std;

void setup_io() {
    freopen("seat.in", "r", stdin);
    freopen("seat.out", "w", stdout);
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    setup_io();

    int n, m;
    cin >> n >> m;

    int total_students = n * m;
    vector<int> scores(total_students);
    for (int i = 0; i < total_students; ++i) {
        cin >> scores[i];
    }
    
    // 根据题意,输入的第一个成绩 a1 是小 R 的成绩
    int target_score = scores[0];

    // 复制一份用于排序,以确定排名
    vector<int> sorted_scores = scores;
    sort(sorted_scores.begin(), sorted_scores.end(), greater<int>());

    // 找到目标分数的排名 (从0开始)
    int rank = -1;
    for (int i = 0; i < total_students; ++i) {
        if (sorted_scores[i] == target_score) {
            rank = i;
            break;
        }
    }

    // 计算0-indexed的列号和行号
    int col_0 = rank / n;
    int pos_in_col = rank % n;
    int row_0;

    if (col_0 % 2 == 0) { // 奇数列 (1, 3, ...),从上到下
        row_0 = pos_in_col;
    } else { // 偶数列 (2, 4, ...),从下到上
        row_0 = n - 1 - pos_in_col;
    }

    // 输出1-indexed的结果
    cout << col_0 + 1 << " " << row_0 + 1 << endl;

    return 0;
}


    第三题:异或和 (xor)
    思路分析
    题目要求在一个序列中找出最多数量的、互不相交的、且区间内所有元素的异或和都等于
    的区间。

    这是一个经典的动态规划问题。为了高效地计算任意区间的异或和,我们可以使用 前缀异或和。

    代码

    #include <bits/stdc++.h>
    
    using namespace std;
    
    void setup_io() {
        freopen("xor.in", "r", stdin);
        freopen("xor.out", "w", stdout);
    }
    
    int main() {
        ios_base::sync_with_stdio(false);
        cin.tie(NULL);
    
        setup_io();
    
        int n;
        int k;
        cin >> n >> k;
    
        vector<int> a(n);
        for (int i = 0; i < n; ++i) {
            cin >> a[i];
        }
    
        vector<int> dp(n + 1, 0);
        // map[pxor] = max_dp_value, 记录前缀异或和为pxor时,能达到的最大dp值
        map<int, int> max_dp_for_pxor;
        max_dp_for_pxor[0] = 0; // 空前缀异或和为0,dp值为0
    
        int current_prefix_xor = 0;
        for (int i = 1; i <= n; ++i) {
            current_prefix_xor ^= a[i - 1];
            
            // 策略1: 不选择以 a[i-1] 结尾的区间
            dp[i] = dp[i - 1];
    
            // 策略2: 寻找一个以 a[i-1] 结尾的合法区间
            int target_pxor = current_prefix_xor ^ k;
            if (max_dp_for_pxor.count(target_pxor)) {
                dp[i] = max(dp[i], max_dp_for_pxor[target_pxor] + 1);
            }
    
            // 更新 map,为后续的计算提供信息
            if (max_dp_for_pxor.count(current_prefix_xor)) {
                max_dp_for_pxor[current_prefix_xor] = max(max_dp_for_pxor[current_prefix_xor], dp[i]);
            } else {
                max_dp_for_pxor[current_prefix_xor] = dp[i];
            }
        }
    
        cout << dp[n] << endl;
    
        return 0;
    }
    
    
    

    第四题:多边形 (polygon)
    题目描述
    小 R 喜欢玩小木棍。小 R 有 n 根小木棍,第 i (1≤i≤n) 根小木棍的长度为 a【i】
    i

    小 X 希望小 R 从这 n 根小木棍中选出若干根小木棍,将它们按任意顺序首尾相连拼成一个多边形。小 R 并不知道小木棍能拼成多边形的条件,于是小 X 直接将条件告诉了他:对于长度分别为 l1,l2 ,…,l m的 m 根小木棍,这 m 根小木棍能拼成一个多边形当且仅当 m≥3 且所有小木棍的长度之和大于所有小木棍的长度最大值的两倍

    由于小 R 知道了小木棍能拼成多边形的条件,小 X 提出了一个更难的问题:有多少种选择小木棍的方案,使得选出的小木棍能够拼成一个多边形?你需要帮助小 R 求出选出的小木棍能够拼成一个多边形的方案数。两种方案不同当且仅当选择的小木棍的下标集合不同,即存在 1≤i≤n,使得其中一种方案选择了第 i 根小木棍,但另一种方案未选择。由于答案可能较大,你只需要求出答案对 998,244,353 取模后的结果。

    输入格式
    输入的第一行包含一个正整数 n,表示小 R 的小木棍的数量。

    输入的第二行包含 n 个正整数 a1,a2,…,an,表示小 R 的小木棍的长度。

    输出格式
    输出一行一个非负整数,表示小 R 选出的小木棍能够拼成一个多边形的方案数对 998,244,353 取模后的结果。

    输入输出样例
    输入

    5
    1 2 3 4 5
    输出

    9
    输入

    5
    2 2 3 8 10
    输出

    6
    说明/提示
    【样例 1 解释】
    共有以下 9 种选择小木棍的方案,使得选出的小木棍能够拼成一个多边形:

    选择第 2,3,4 根小木棍,长度之和为 2+3+4=9,长度最大值为 4;
    选择第 2,4,5 根小木棍,长度之和为 2+4+5=11,长度最大值为 5;
    选择第 3,4,5 根小木棍,长度之和为 3+4+5=12,长度最大值为 5;
    选择第 1,2,3,4 根小木棍,长度之和为 1+2+3+4=10,长度最大值为 4;
    选择第 1,2,3,5 根小木棍,长度之和为 1+2+3+5=11,长度最大值为 5;
    选择第 1,2,4,5 根小木棍,长度之和为 1+2+4+5=12,长度最大值为 5;
    选择第 1,3,4,5 根小木棍,长度之和为 1+3+4+5=13,长度最大值为 5;
    选择第 2,3,4,5 根小木棍,长度之和为 2+3+4+5=14,长度最大值为 5;
    选择第 1,2,3,4,5 根小木棍,长度之和为 1+2+3+4+5=15,长度最大值为 5。
    【样例 2 解释】
    共有以下 6 种选择小木棍的方案,使得选出的小木棍能够拼成一个多边形:

    选择第 1,2,3 根小木棍,长度之和为 2+2+3=7,长度最大值为 3;
    选择第 3,4,5 根小木棍,长度之和为 3+8+10=21,长度最大值为 10;
    选择第 1,2,4,5 根小木棍,长度之和为 2+2+8+10=22,长度最大值为 10;
    选择第 1,3,4,5 根小木棍,长度之和为 2+3+8+10=23,长度最大值为 10;
    选择第 2,3,4,5 根小木棍,长度之和为 2+3+8+10=23,长度最大值为 10;
    选择第 1,2,3,4,5 根小木棍,长度之和为 2+2+3+8+10=25,长度最大值为 10。
    【样例 3】
    见选手目录下的 polygon/polygon3.in 与 polygon/polygon3.ans。

    该样例满足测试点 7∼10 的约束条件。

    【样例 4】
    见选手目录下的 polygon/polygon4.in 与 polygon/polygon4.ans。

    该样例满足测试点 11∼14 的约束条件。

    【子任务】
    对于所有测试数据,保证:

    3≤n≤5,000;
    对于所有 1≤i≤n,均有 1≤a
    i

    ≤5000。
    测试点编号 n≤ max
    i=1
    n

    a
    i


    1∼3 3 10
    4∼6 10 10
    2

    7∼10 20
    11∼14 500
    15∼17 1
    18∼20 5000
    21∼25 5000

    代码

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int MOD = 998244353;
    
    void setup_io() {
        freopen("polygon.in", "r", stdin);
        freopen("polygon.out", "w", stdout);
    }
    
    int main() {
        ios_base::sync_with_stdio(false);
        cin.tie(NULL);
    
        setup_io();
    
        int n;
        cin >> n;
    
        vector<int> a(n);
        for (int i = 0; i < n; ++i) {
            cin >> a[i];
        }
    
        sort(a.begin(), a.end());
    
        long long total_ans = 0;
        // dp[s]: 和为s的方案数。a[i]最大5000,我们只需统计和不大于5000的方案,所以数组大小开到5001。
        vector<long long> dp(5001, 0); 
        dp[0] = 1;
    
        // 预计算2的幂,避免重复计算
        vector<long long> pow2(n + 1);
        pow2[0] = 1;
        for(int i = 1; i <= n; ++i) {
            pow2[i] = (pow2[i-1] * 2) % MOD;
        }
    
        long long current_prefix_sum = 0;
        for (int i = 0; i < n; ++i) {
            // Step 1: 以 a[i] 为最长边,从 a[0]...a[i-1] 中选边
            // 计算 sum(dp[s]) for s from 0 to a[i]
            long long subsets_le_ai = 0;
            for (int s = 0; s <= a[i]; ++s) {
                subsets_le_ai = (subsets_le_ai + dp[s]) % MOD;
            }
            
            // 总子集数是 2^i
            long long total_subsets_prefix = pow2[i];
            // 和 > a[i] 的子集数
            long long good_subsets_count = (total_subsets_prefix - subsets_le_ai + MOD) % MOD;
            
            total_ans = (total_ans + good_subsets_count) % MOD;
    
            // Step 2: 更新 dp 数组,将 a[i] 加入选择池
            current_prefix_sum += a[i];
            for (int s = min((long long)5000, current_prefix_sum); s >= a[i]; --s) {
                dp[s] = (dp[s] + dp[s - a[i]]) % MOD;
            }
        }
    
        cout << total_ans << endl;
    
        return 0;
    }
    
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值