//https://ac.nowcoder.com/acm/contest/11225/C
//还是需要题解,不然实在想不到。。。
//区间的题目确实恶心人,不重叠的话可以排序,重叠的话,单点查询的话可以用前缀和,但是如果是区间查询的话,一个前缀和数组就无法确认一个区间和多少个区间重合。
//做法:开两个数组l, r分别存储左端点和右端点出现的次数。
//要查询某个区间(区间长度为len)与其他区间交叉的个数,只要l[1] + L[2] + L[3] + ... + l[i] - (r[1] + r[2] + r[3] + ... + r[i - len])就行。
//然后就是这道题最重要的思路:[l, r] 与 [l + 1, r + 1] 这两个长度相同的区间的差别仅仅在于 l和 r + 1处。
//这里运用了滑动窗口的思想,这样的话O(n)遍历即可通过。。(滑动窗口的题(单调队列)https://www.acwing.com/problem/content/description/156/)
//先算出[1, len]的交叉值,后面遍历时每次判断两个即可。至于权重也可两个数组保存,只不过是位号相加。
//易错点:len 5e6, l、r 5e6,如果考虑最边上有个区间的情况下,需要遍历到1e7才行。 当然还要longlong。
#include<iostream>
using namespace std;
typedef long long ll;
const int maxn = 1e7 + 10;
ll ln[maxn], rn[maxn], lv[maxn], rv[maxn];
int main() {
int n, len;
cin >> n >> len;
for(int i = 1; i <= n; i++) {
int l, r;
cin >> l >> r;
ln[l]++, rn[r]++;
lv[l] += i, rv[r] += i;
}
ll cnt = 0, value = 0;
for(int i = 1; i <= len; i++) { //[1, len]的交叉值直接把左端点数组加起来即可。
cnt += ln[i];
value += lv[i];
}
ll m1 = cnt, m2 = value, ans = 1; //这里ans为1。
for(int i = len + 1; i <= maxn; i++) {
cnt += ln[i], cnt -= rn[i - len];
value += lv[i], value -= rv[i - len];
//cout << "i = " << i << ", cnt = "<< cnt << ", value = " << value << "\n";
if(cnt > m1 || (cnt == m1 && value > m2)) m1 = cnt, m2 = value, ans = 0;
if(cnt == m1 && value == m2) ans++;
}
cout << ans << "\n";
}
牛客xiao白月赛48 C可疑的区间
最新推荐文章于 2024-06-15 23:36:03 发布