A题
题目解读
给定一个三位数 N N N,其中每个数字都在 1 到 9 之间。我们需要分别提取百位、十位和个位的数字 a a a、 b b b、 c c c,然后按指定的顺序打印出两个新整数:
- 先输出 b b b, c c c, a a a 构成的整数。
- 然后输出 c c c, a a a, b b b 构成的整数。
解题思路
可以通过整数运算得到百位、十位和个位的数字:
- 百位数 a a a 可以通过整数除法 N / 100 N /100 N/100 得到。
- 十位数 b b b 可以通过先 N / 10 N / 10 N/10 得到去除百位后的数,再对 10 取余 (N / 10) % 10。
- 个位数 c c c 可以通过 N N % 10 N 直接得到。
通过整数运算提取各位数字,根据要求的顺序重新组合数字,并打印结果。
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
int N;
cin >> N;
int a = N / 100;
int b = (N / 10) % 10;
int c = N % 10;
cout << b * 100 + c * 10 + a << " " << c * 100 + a * 10 + b << endl;
return 0;
}
B题
题目解读
高桥有 N N N 颗牙齿,牙齿的健康状况用一个字符串 S S S 表示,字符 “O” 代表健康牙齿,“X” 代表有蛀牙。如果有 K K K 颗连续健康的牙齿,吃掉草莓后这些牙齿就会变成蛀牙。我们需要计算高桥最多可以吃多少颗草莓。
解题思路
需要遍历字符串 S S S,找出每段连续的 “O” 字符串:
- 对每段连续的健康牙齿,如果其长度大于等于 K K K,则最多能吃 floor ( 长度 / K ) \text{floor}(\text{长度} / K) floor(长度/K) 颗草莓。
- 累加所有能吃草莓的数量。
- 通过遍历字符串来查找每段连续的 “O”。每段 “O” 的长度除以 K K K 就是可以吃的草莓数。注意最后一段连续的 "O"需要在遍历结束后处理。
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
int N, K;
string S;
cin >> N >> K >> S;
int count = 0, total = 0;
for (int i = 0; i < N; i++) {
if (S[i] == 'O') {
count++;
} else {
total += count / K;
count = 0;
}
}
// 最后一个连续 "O" 的部分
total += count / K;
cout << total << endl;
return 0;
}
C题
解题思路
为了解决这个问题,我们需要确认是否可能通过一系列操作,使得每个单元格中都至少含有一块石头。这要求我们满足两个关键条件:
- 所有单元格中的石头总数必须等于 N N N,即 ∑ i = 1 M A i = N \sum_{i=1}^{M} A_i = N ∑i=1MAi=N。
- 对于每个 i i i( 1 ≤ i ≤ M 1 \leq i \leq M 1≤i≤M),前 i i i 个单元格中的石头总数应至少为 i i i。
由于直接检查每个 i i i 的条件可能非常耗时,我们利用排序和累计和的技巧来优化计算过程。具体来说,我们只需关注那些会改变前 i i i 个单元格中石头总数的索引,即 X 1 , X 2 , … , X M X_1, X_2, \ldots, X_M X1,X2,…,XM,并通过对这些索引进行排序,方便地计算累计和,从而验证条件是否满足。
在确认满足上述条件后,我们接下来计算达到每个单元格都至少有一块石头所需的最小操作次数。这个计算基于索引总和的差值,公式为:
最小操作次数 = N ( N + 1 ) 2 − ∑ i = 1 M X i × A i \text{最小操作次数} = \frac{N(N+1)}{2} - \sum_{i=1}^{M} X_i \times A_i 最小操作次数=2N(N+1)−i=1∑MXi×Ai
其中, N ( N + 1 ) 2 \frac{N(N+1)}{2} 2N(N+1) 代表如果每个单元格都有一块石头时(即理想状态)的索引总和,而 ∑ i = 1 M X i × A i \sum_{i=1}^{M} X_i \times A_i ∑i=1MXi×Ai 则代表当前石头分布下的索引总和。通过计算这两个总和的差值,我们可以得出所需的最小操作次数。整个算法的计算复杂度为 O ( M log M ) O(M \log M) O(MlogM)。
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
long long n;
int m;
cin >> n >> m;
vector<pair<int, long long>> xa(m);
for (int i = 0; i < m; ++i) {
cin >> xa[i].first;
}
for (int i = 0; i < m; ++i) {
cin >> xa[i].second;
}
sort(xa.begin(), xa.end());
long long sum = 0, sum_idx = 0;
for (int i = 0; i < m; ++i) {
if (sum < xa[i].first - 1) {
cout << -1 << endl;
return 0;
}
sum += xa[i].second;
sum_idx += xa[i].second * xa[i].first;
}
if (sum != n) {
cout << -1 << endl;
return 0;
}
cout << n * (n + 1) / 2 - sum_idx << endl;
}
D题
解题思路
作为一种直接的解决方案,直接管理植物和它们的高度可能会导致 O ( Q 2 ) O(Q^2) O(Q2) 的时间复杂度,这在处理大量查询时并不高效。瓶颈主要在于第二类查询,即要求将每株植物的高度增加 H H H。
通过观察与推理,我们可以发现以下优化策略来处理植物的查询问题:
首先,我们设 A i A_i Ai 为最初种植的植物在处理第 i i i 次查询时的高度(如果它没有被收割),并注意到被先前查询种植过的植物数量较多。对于高度变化,如果第 i i i 次查询是增加高度的第二类查询,则 A i A_i Ai 会更新为 A i − 1 + T A_{i-1} + T Ai−1+T,否则 A i A_i Ai 保持为 A i − 1 A_{i-1} Ai−1。在处理第一和第三种查询时,我们使用查询索引来管理植物,而不是直接使用植物的高度,这样可以更高效地处理第一类查询(种植)的插入操作。对于第三类查询(收割),我们按照查询索引的顺序扫描每棵植物,只收割高度大于或等于 H H H 的植物,并且每株植物只收割一次,最多收割 Q Q Q 次。为了高效地管理植物,我们使用队列来插入和删除植物。在每次第三种类型的查询中,我们最多检查一次未立即收割的植物,总计 Q Q Q 次,从而确保处理时间为 O ( Q ) O(Q) O(Q)。
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
int q;
cin >> q;
queue<int> que;
vector<long long> height(q + 1);
height[0] = 0;
for (int i = 0; i < q; ++i) {
int t;
cin >> t;
if (t == 1) {
height[i + 1] = height[i];
que.push(i);
}
if (t == 2) {
long long add;
cin >> add;
height[i + 1] = height[i] + add;
}
if (t == 3) {
height[i + 1] = height[i];
long long h;
cin >> h;
int ans = 0;
while (!que.empty()) {
if (height[i + 1] - height[que.front()] >= h) {
ans++;
que.pop();
} else {
break;
}
}
cout << ans << "\n";
}
}
}