本文为AtCoder Beginner Contest 414 A - E的详细题解
目录
题目A:
题目大意:
一个主播从L点播到R点,现在有n个观众,可以在xi到yi的时间内观看直播
现在问这n个观众中有多少个观众可以完整的看完整个直播
解题思路:
对于一个观众来说,要想看完完整的直播
也就是说,能观看直播的时间[xi, yi]是要包含主播的开播时间区间[L, R]
也就是要满足xi <= L && R <= yi。
代码(C++):
void solve() {
int N, L, R;
std::cin >> N >> L >> R;
int ans = 0;
for (int i = 0; i < N; i++) {
int X, Y;
std::cin >> X >> Y;
if (X <= L && R <= Y) {
ans++;
}
}
std::cout << ans << "\n";
}
题目B:
题目大意:
现在有n对字符和整数,分别为(C1, L1),(C2, L2),(C3, L3)...(CN, LN),
现在有一个空字符串S,你需要依次在这个空字符串上面加L1个C1字符,L2个C2字符,L3个C3字符...LN个CN字符。
现在你需要最后的字符串S。
但是如果S的长度大于100,就输出字符串“Too Long”
解题思路:
根据题目大意进行模拟即可,也就是创建一个空字符串,然后每次加上Li个C1字符。
注意的点是,由于长度大于100就输出字符串"Too Long",并且L的取值范围很大
我们可以在进行操作的时候,如果L的大小大于100的话,就直接输出"Too Long"然后退出即可
代码(C++):
void solve() {
int N;
std::cin >> N;
std::vector<std::pair<char, int>> a(N);
for (int i = 0; i < N; i++) {
char c;
int l;
std::cin >> c >> l;
a.push_back({c, l});
}
std::string s = "";
for (auto& [c, l] : a) {
if (l > 100) {
std::cout << "Too Long\n";
return;
}
std::string cur(l, c);
s += cur;
if (s.size() > 100) {
std::cout << "Too Long\n";
return;
}
}
std::cout << s << "\n";
}
题目C:
题目大意:
现在给你一个整数N,你需要找出从1到N这N个整数中,满足十进制和A进制表示都是回文数
N的最大值为1e12
解题思路:
题目条件:数字满足十进制和A进制都表示的是回文数。
这题首先想到的枚举思路可能是:从1枚举到N然后判断回文数,然后再判断题目条件
对于判断回文数和判断题目条件操作的时间复杂度都可以视为O(1),也就是总复杂度是O(N)
但是N的取值是1e12,肯定超时
那么我们开始优化,我们可以直接枚举1 到 N里面的回文数(十进制)。
可以用如下的枚举方法,由于是回文数字,我们只需要枚举到N的一半就行了,另外一半直接根据前一半生成就行了。
N最大为1e12,也就是我们枚举到1e6就行了。
也就是现在的时间复杂度为O(N / 2)
具体的实现可以看代码。
代码(C++):
void solve() {
int A;
i64 N;
std::cin >> A >> N;
std::vector<i64> pow10(8, 1);
for (int i = 1; i <= 7; i++) {
pow10[i] = pow10[i - 1] * 10;
}
i64 ans = 0;
auto build = [&](i64 l, bool odd) -> i64 {
i64 x = l;
if (odd) {
l /= 10;
}
while (l > 0) {
x = x * 10 + l % 10;
l /= 10;
}
return x;
};
auto check2 = [&](i64 x, int A) -> bool {
std::vector<int> d(64);
int n = 0;
while (x > 0) {
d[n] = x % A;
n++;
x /= A;
}
if (n == 0) {
d[n++] = 0;
}
for (int i = 0; i < n / 2; i++) {
if (d[i] != d[n - i - 1]) {
return false;
}
}
return true;
};
for (int len = 1; len <= 7; len++) {
i64 L = pow10[len - 1];
i64 R = pow10[len] - 1;
for (i64 h = L; h <= R; h++) {
i64 p1 = build(h, 1);
if (p1 > N) {
std::cout << ans << "\n";
return;
}
if (p1 <= N && check2(p1, A)) {
ans += p1;
}
i64 p2 = build(h, 0);
if (p2 <= N && check2(p2, A)) {
ans += p2;
}
}
}
std::cout << ans << "\n";
}
题目D:
题目大意:
现在有一条坐标轴,坐标轴上面有房子,现在有N个房子,第i个房子的坐标为Xi。
现在你需要在坐标轴上面放基站,一个强度信号为x的基站,可以覆盖方圆x / 2的房子。
现在你需要在坐标轴上放M个基站(你可以放在任意位置),并且给每个基站设定一个信号强度,
现在你的目标是,在保证N个房子都被基站覆盖的情况下,让总的基站强度最小。
输出这个最小值。
解题思路:
我们观察题目条件:一个强度信号为x的基站,可以覆盖方圆x / 2的房子。
我们可以转换成,一个信号强度为x的基站,可以看作一条长度为x的线段,基站在线段中点,线段内的房屋都可以被覆盖。
我们既然要覆盖这X个房子,我们先对这X个房子的坐标进行排序,好进行后面的处理。
现在这X个房子,我们可以看成X - 1条线段。
现在我们的目标是,有M条线段,我们要给这M条线段设置长度,让其覆盖N个点,并且保证M条线段的长度总和尽可能的小。
我们可以这么做,先假设M条线段的长度总和为X[N - 1] - X[0]。
由于我们只需要覆盖点,那么我们可以从中剔除两个房子所连成的线段,可以证明最多只能剔除
N -M个。
我们只需要剔除长度最大的那N - M 个线段即可。
具体实现可以看代码
代码(C++):
void solve() {
int N, M;
std::cin >> N >> M;
std::vector<i64> X(N);
for (int i = 0; i < N; i++) {
std::cin >> X[i];
}
std::ranges::sort(X);
std::vector<i64> diff(N - 1);
for (int i = 0; i < N - 1; i++) {
diff[i] = X[i + 1] - X[i];
}
std::ranges::sort(diff);
i64 ans = 0;
for (int i = 0; i < N - M; i++) {
ans += diff[i];
}
std::cout << ans;
}
题目E:
题目大意:
现在你需要找出这样一个数对(a, b, c)的数量,两两不相同,并且都在1 到 N 内。
满足a % b == c。
输出满足这样的数对的数量
解题思路:
我们可以发现,我们可以这么求,(a, b)数对的数目为N * (N - 1) / 2。
我们只需要减去里面不满足的数目即可,不满足的数目为枚举i从1到N,N / i的总和。
但是N的大小为1e12,这样做还是会超时。
我们可以用整除分块进行优化
具体实现可以参考代码(这里我参考星子哥写法Submission #67528319 - Mirrativ Programming Contest 2025 (AtCoder Beginner Contest 414))
代码(C++):
using i64 = long long;
const int mod = 998244353;
const int inv2 = (mod + 1) / 2; // 2的逆元
void solve() {
i64 n;
std::cin >> n;
i64 ans;
// 计算 N(N+1)/2 mod 998244353
ans = n % mod * ((n + 1) % mod) % mod * inv2 % mod;
// 整除分块计算
i64 i = 1;
while (i <= n) {
i64 d = n / i;
i64 nxt = n / d;
// 计算当前块贡献
i64 len = (nxt - i + 1) % mod;
i64 term = (d % mod) * len % mod;
// 减去块内贡献
ans = (ans - term + mod) % mod;
i = nxt + 1; //跳到下个块起始位置
}
std::cout << ans << '\n';
}
Python写法:
题目A:
def solve():
N, L, R = MII()
ans = 0
for _ in range(N):
X, Y = MII()
if X <= L and R <= Y:
ans += 1
print(ans)
题目B:
def solve():
n = II()
a = []
for _ in range(n):
c, l = input().split()
a.append([c, int(l)])
s = ""
for c, l in a:
if l > 100 or len(s) > 100:
print("Too Long")
return
s += c * l
print(s if len(s) <= 100 else "Too Long")
题目C:
def solve():
A = II()
N = II()
pow10 = [10**i for i in range(8)]
def build(num: int, odd: bool) -> int:
x = num
if odd:
num //= 10
while num > 0:
x = x * 10 + num % 10
num //= 10
return x
def check(num: int) -> bool:
d = []
while num > 0:
d.append(num % A)
num //= A
n = len(d)
for i in range(n // 2):
if d[i] != d[n - 1 - i]:
return False
return True
ans = 0
for i in range(1, 8):
L = pow10[i - 1]
R = pow10[i]
for num in range(L, R):
num1 = build(num, 1)
if num1 > N:
print(ans)
return
if check(num1):
ans += num1
num2 = build(num, 0)
if num2 <= N and check(num2):
ans += num2
print(ans)
题目D:
def solve():
N, M = MII()
X = LII()
X.sort()
diff = [0] * (N - 1)
for i in range(N - 1):
diff[i] = X[i + 1] - X[i]
diff.sort()
ans = 0
for i in range(N - M):
ans += diff[i]
print(ans)