#include <bits/stdc++.h>
using namespace std;
#define int long long
struct u {
int l, r;
};
bool cmp(u x, u y) {
if (x.r != y.r) return x.r < y.r;
return x.l < y.l;
}
bool cmp2(u x, u y) {
if (x.l != y.l) return x.l < y.l;
return x.r > y.r;
}
signed main() {
int n, k;
cin >> n >> k;
vector<u> ans(n);
for (int i = 0; i < n; i++) {
cin >> ans[i].l >> ans[i].r;
}
// 情况1:按右端点排序,枚举L = 右端点
sort(ans.begin(), ans.end(), cmp);
priority_queue<int, vector<int>, greater<int>> q1;
int biao1 = 0;
int maxw1 = 0;
for (int id = 0; id < n; id++) {
int window_right = ans[id].r + k;
while (!q1.empty() && q1.top() < ans[id].r) q1.pop();
while (biao1 < n && ans[biao1].l <= window_right) {
q1.push(ans[biao1].r);
biao1++;
}
maxw1 = max(maxw1, (int)q1.size());//q.size()用int存储之后再比较
}
// 情况2:按左端点排序,枚举L = 左端点
sort(ans.begin(), ans.end(), cmp2);
priority_queue<int, vector<int>, greater<int>> q2;
int biao2 = 0;
int maxw2 = 0;
for (int id = 0; id < n; id++) {
int window_left = ans[id].l - k;
// 这里窗口左边界是 ans[id].l,需要删除右端点 < ans[id].l 的区间
while (!q2.empty() && q2.top() < window_left) q2.pop();
// 加入所有左端点 <= window_right 的区间(这里左端点就是 ans[biao2].l,已经有序)
while (biao2 < n && ans[biao2].l <= ans[id].l) {
q2.push(ans[biao2].r);
biao2++;
}
maxw2 = max(maxw2, (int)q2.size());
}
cout << max(maxw1, maxw2) << endl;
return 0;
}
这个题本质上是扫面线,但我是用堆来实现的这个扫描。
该说不说这个题用堆来做,确确实实有很多要注意的小细节。
第一个需要注意的是贪心.
首先我们看区间大小,0到1e9,不可能枚举暴力做,接下来我们去看n的范围0到2e5可以接受,我们深思之下,会想到枚举所有所给区间的右端点来作为所选区间的起点,
为何如此,因为要取得最好的结果,我们尽量得让区间延申出去,例如一个区间[1,5],如果所给k为2,那么可以选择[1,3],[2,4],[3,5]....,最佳情况肯定是[5,7]因为这时候卡在相交的边缘,做到了尽量让区间延申出去,可能会有这样的疑惑,如果所给区间[1,5],[2,3],[3,4]屯在一块这时候好像不能延申,其实并不正确延申是通用的,我们只需要对r从小到大排序就可以了,这样的话对于每一个从小到大的r我们都尽量往外延申,则可以达到最优解
这个过程需要用堆来实现,堆是最小堆,维护的是区间的右端点值,维护该值主要是为了,弹出过期元素。
因为每个元素顶多出队入队一次,时间复杂度o(nlogn)
如果仅仅想到这里,你会发现你只能对75%的样例,那另外错哪里了,很简单你同时也应该每个区间左端点来作为所选区间的终点,这时候同样也满足区间尽量向外申的根本目的,所以他也可能产生最优解,所以要做两遍扫描线
问题就解决了
但别忘了,在排序的时候,如果初步按r从小到大排序,则相同r,l应该按从小到大排序,这样你会先遍历到l小的,如果l小的不满足就可以break了,如果l从大到小 l不满足跳过逻辑就不对了,因为小的l可能满足
但是先按从小到大排序则不需要关注这个问题,大家可以想想为什么嘻嘻嘻

京公网安备 11010502036488号