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

1852

被折叠的 条评论
为什么被折叠?



