abc271_c Manga 题解
Manga
题意
有一部连载漫画,共 \(10^9\) 卷,你手上有 \(n\) 卷漫画,第 \(i\) 卷是连载中的第 \(a_i\) 卷。
你在看漫画之前,可以执行以下操作若干次(可以不执行):
- 若手上还有至少两卷漫画,可以卖掉任意两卷,并买一卷任意卷数的漫画。
你不想看不连续的漫画,所以你只能从连载第一卷开始读,直到你手上没有连载的下一卷,问你最多可以读多少卷。
数据范围
- \(1 \leqslant n \leqslant 3 \times 10^5\)
- \(1 \leqslant a_i \leqslant 10^9\)
思路
首先,我们手上会有重复卷数的漫画,这些废卷可以直接卖掉。
然后就是一个双指针,当手上没有废卷而且还有空缺时,我们可以拿手上卷数最大的漫画(排序)去补空缺,有些细节,总体难度不大。
当然你也可以二分,二分最多能阅读几卷,check
函数详见代码。
复杂度
- 时间:\(O(n \log n)\)
- 空间:\(O(n)\)
Code
排序双指针代码
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 3e5 + 10;
int n, m, a[N], b[N], ans, now = 1, sum;
int main () {
ios::sync_with_stdio(0), cin.tie(0);
cin >> m;
for (int i = 1; i <= m; i++) {
cin >> b[i];
}
sort(b + 1, b + m + 1);
for (int i = 1; i <= m; i++) {
if (b[i] == b[i - 1]) { // 废卷
sum++; // 统计废卷卷数
} else {
a[++n] = b[i];
}
}
for (int i = 1, j = n; i <= j; ) {
if (a[i] != now) { // 现在考虑的这本不能接上上一卷
if (sum >= 2) { // 优先使用废卷
sum -= 2;
} else if (sum) { // 废卷还剩一本,用掉废卷和最后的一卷
j--, sum = 0;
} else if (j - i >= 1) { // 还有可以卖掉的
j -= 2; // 卖掉最大的两卷
} else {
break; // 退出
}
} else {
i++;
}
ans++, now++;
}
cout << ans + sum / 2; // 可能还有废卷,也要卖掉
return 0;
}
二分代码
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 3e5 + 10;
int n, m, a[N], b[N], ans, now = 1, sum, l, r;
bool check (int x) {
int num = sum, cnt = x; // num 统计可以卖掉的卷数,cnt 统计空缺数
for (int i = 1; i <= n; i++) {
if (a[i] <= x) { // 在前 x 卷中
cnt--; // 空缺数减 1
} else {
num++; // 卖掉它
}
}
return (cnt * 2 <= num);
}
int main () {
ios::sync_with_stdio(0), cin.tie(0);
cin >> m;
r = m;
for (int i = 1; i <= m; i++) {
cin >> b[i];
}
sort(b + 1, b + m + 1);
for (int i = 1; i <= m; i++) {
if (b[i] == b[i - 1]) { // 废卷
sum++; // 统计废卷卷数
} else {
a[++n] = b[i];
}
}
while (l < r) {
int mid = (l + r + 1) >> 1;
if (check(mid)) {
l = mid;
} else {
r = mid - 1;
}
}
cout << l;
return 0;
}