- CodeRush Round 2(Div. 4)
CodeRush Round 2(Div. 4) 题解
- @ 2026-4-21 0:21:27
A. 睡觉大王(PID 5690)
题目简介
你今天有 n 节课,用一个 01 字符串表示。s_i = 1 表示这是重要课程,必须清醒听课。听完一节重要课后,接下来的 k 节课你也必须保持清醒。其余课程可以逃课睡觉。问最多能睡几节课。
思路分析
直接模拟即可。从左到右扫描每一节课:
- 如果当前课程是重要课(s_i = 1),必须清醒,同时设置一个"强制清醒计时器"为 k。
- 如果当前课程不是重要课:
- 计时器 > 0:虽然不想听,但被迫保持清醒,计时器减 1。
- 计时器 = 0:可以睡觉,答案加 1。
时间复杂度 O(n),非常高效。
C++ 代码
#include <iostream>
#include <cstdio>
using namespace std;
int main() {
int t;
scanf("%d", &t);
while (t--) {
int n, k;
scanf("%d%d", &n, &k);
char s[105];
scanf("%s", s);
int ans = 0; // 能睡觉的课数
int timer = 0; // 强制清醒的剩余时长
for (int i = 0; i < n; i++) {
if (s[i] == '1') {
// 重要课程,必须清醒,并重置计时器
timer = k;
} else {
if (timer > 0) {
// 还在强制清醒期间,不能睡
timer--;
} else {
// 可以睡觉!
ans++;
}
}
}
printf("%d\n", ans);
}
return 0;
}
B. 日期差值计算(PID 5687)
题目简介
输入两个合法日期(格式 YYYY-MM-DD,范围 0001~2099),计算两个日期之间相差多少天。
思路分析
将每个日期转换为从公元 1 年 1 月 1 日起的天数,然后取差的绝对值。
- 年份贡献:遍历 1 到 year-1 年,闰年加 366 天,平年加 365 天。
- 月份贡献:累加当年 1 到 month-1 月的天数。
- 日期贡献:加上 day。
闰年判断:能被 4 整除且不能被 100 整除,或能被 400 整除。
注意:1900 年能被 4 整除,但也能被 100 整除且不能被 400 整除,所以 1900 年是平年(2 月只有 28 天)!
C++ 代码
#include <iostream>
#include <cstdio>
using namespace std;
// 判断是否为闰年
bool isLeap(int y) {
return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0);
}
// 每月天数,下标1~12
int daysInMonth[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
// 计算从公元1年1月1日起,该日期是第几天
long long dateToDays(int y, int m, int d) {
long long days = 0;
// 累加之前每年的天数
for (int i = 1; i < y; i++) {
days += isLeap(i) ? 366 : 365;
}
// 累加当年之前每月的天数
for (int i = 1; i < m; i++) {
if (i == 2 && isLeap(y)) {
days += 29; // 闰年2月有29天
} else {
days += daysInMonth[i];
}
}
// 加上当月的天数
days += d;
return days;
}
int main() {
int y1, m1, d1, y2, m2, d2;
// 按 YYYY-MM-DD 格式读入
scanf("%d-%d-%d", &y1, &m1, &d1);
scanf("%d-%d-%d", &y2, &m2, &d2);
long long days1 = dateToDays(y1, m1, d1);
long long days2 = dateToDays(y2, m2, d2);
// 输出绝对差值
printf("%lld\n", days1 > days2 ? days1 - days2 : days2 - days1);
return 0;
}
C. 宝藏寻宝游戏(PID 4907)
题目简介
给定一个长度为 n 的序列,q 次询问。每次询问给定区间 [l, r],求区间内所有正数的和(可以选不连续的元素,但只选正数才能使和最大)。
样例验证:序列 [2, -1, 2, 3, -5, 2]
- 区间 [1,2]:正数只有 a₁=2,答案 = 2
- 区间 [1,3]:正数有 a₁=2, a₃=2,答案 = 2+2 = 4
- 区间 [2,4]:正数有 a₃=2, a₄=3,答案 = 2+3 = 5
思路分析
预处理前缀和:设 pre[i] 表示前 i 个元素中所有正数的和。
pre[i] = pre[i-1] + max(0, a[i])
对于每次询问 [l, r],答案就是 pre[r] - pre[l-1]。
- 时间复杂度:预处理 O(n),每次查询 O(1),总计 O(n + q)。
- 数据范围 n, q ≤ 10⁶,|aᵢ| ≤ 10⁹,最大答案约 10¹⁵,需要用 long long。
C++ 代码
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN = 1000005;
long long pre[MAXN]; // 前缀和:前i个数中正数的和
int main() {
int n;
scanf("%d", &n);
// 读入序列,同时计算正数的前缀和
for (int i = 1; i <= n; i++) {
long long x;
scanf("%lld", &x);
if (x > 0) {
pre[i] = pre[i - 1] + x; // 正数才累加
} else {
pre[i] = pre[i - 1]; // 非正数跳过
}
}
int q;
scanf("%d", &q);
while (q--) {
int l, r;
scanf("%d%d", &l, &r);
// 区间 [l, r] 内正数的和 = 前缀和之差
printf("%lld\n", pre[r] - pre[l - 1]);
}
return 0;
}
D. 卡牌游戏(PID 5691)
题目简介
有两副牌组 A 和 B(由 0~9 的数字组成)。可以对 A 进行两种操作:
- 全局数字替换:选择两个不同数字 n₁ 和 n₂,将 A 中所有 n₁ 换成 n₂,所有 n₂ 换成 n₁。
- 位置交换:交换 A 中任意两张牌的位置。
问能否通过若干次操作使 A 变成 B。
思路分析
核心观察:
- 操作 1 可以对 10 种数字做任意排列(多次操作 1 可以组合出任意置换)。
- 操作 2 可以对牌的位置做任意排列。
因此,A 能变成 B 当且仅当:对 A 的每种数字的出现次数排序后,与 B 的排序结果相同。
做法:统计 A 和 B 各自 0~9 每种数字的出现次数,分别排序后比较是否相同。
C++ 代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int main() {
int t;
scanf("%d", &t);
while (t--) {
char a[200005], b[200005];
scanf("%s%s", a, b);
// 统计 A 和 B 中各数字的出现次数
int cntA[10] = {0}, cntB[10] = {0};
int len = strlen(a);
for (int i = 0; i < len; i++) {
cntA[a[i] - '0']++;
cntB[b[i] - '0']++;
}
// 排序后比较(只关心每种频率出现几次,不关心是哪个数字)
sort(cntA, cntA + 10);
sort(cntB, cntB + 10);
// 频率分布相同 → 可以互相转换
bool same = true;
for (int i = 0; i < 10; i++) {
if (cntA[i] != cntB[i]) {
same = false;
break;
}
}
printf(same ? "Yes\n" : "No\n");
}
return 0;
}
E. 霸王龙的新符号(PID 5692)
题目简介
定义运算 a++b:将 a 和 b 的十进制数字拼接成新整数(如 14++2 = 142)。给定 n 个正整数,求有多少个无序对 (i, j)(i ≠ j)使得 nums[i]++nums[j] 是 3 的倍数。
思路分析
关键数学性质:一个整数能被 3 整除 ⟺ 它的各位数字之和能被 3 整除。
拼接操作只是把 a 和 b 的数字连在一起,所以拼接结果的数位和 = a 的数位和 + b 的数位和。
因此:a++b 是 3 的倍数 ⟺ (a 的数位和 + b 的数位和) mod 3 = 0。
做法:
- 对每个数,计算数位和对 3 取模的余数(0、1 或 2)。
- 统计余数为 0、1、2 的数各有多少个(cnt[0], cnt[1], cnt[2])。
- 合法配对:
- 余数 0 + 余数 0:C(cnt[0], 2) = cnt[0] × (cnt[0] - 1) / 2
- 余数 1 + 余数 2:cnt[1] × cnt[2]
- 总数 = 两者之和。
注意:题目中数字可达 10³⁰,必须当字符串读入,不能用整数类型。
C++ 代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int main() {
int n;
scanf("%d", &n);
// cnt[i] 表示数位和 mod 3 == i 的数有多少个
long long cnt[3] = {0, 0, 0};
for (int i = 0; i < n; i++) {
char s[35]; // 数字最大 10^30,最多 31 位
scanf("%s", s);
int len = strlen(s);
int digitSum = 0;
// 逐位累加数位和
for (int j = 0; j < len; j++) {
digitSum += s[j] - '0';
}
// 对 3 取模,归类计数
cnt[digitSum % 3]++;
}
// 余数 0 配 余数 0:从 cnt[0] 个中选 2 个
long long ans = cnt[0] * (cnt[0] - 1) / 2;
// 余数 1 配 余数 2:直接相乘
ans += cnt[1] * cnt[2];
printf("%lld\n", ans);
return 0;
}
总结
| 题目 | 难度 | 算法 | 时间复杂度 |
|---|---|---|---|
| A. 睡觉大王 | ⭐ | 模拟 | O(n) |
| B. 日期差值计算 | ⭐⭐ | 模拟 / 日期处理 | O(y + m) |
| C. 宝藏寻宝游戏 | 前缀和 | O(n + q) | |
| D. 卡牌游戏 | 思维 / 排序 | O(n) | |
| E. 霸王龙的新符号 | ⭐⭐⭐ | 数论 + 计数 | O(n * m) |
3 条评论
-
wengzhaoyu8 LV 9 @ 2026-4-22 13:12:56
-
@ 2026-4-22 12:28:06 -
@ 2026-4-21 16:38:01
T5复杂度是不是错了,令字符串长度为 ,应该为 ,不知道 是从哪里出来的
- 1