题意
数轴上有一共
个点,
个区间分别是
。
设为所选取的区间数量,
为所有所选取的区间的交集长度。
求的最大值。
最优解标程为线段树,复杂度为
qingzhu思路
- 将所有线段按左端点升序排序。
- 枚举区间:
枚举左端点
,二分
枚举右端点。
- 收纳所有左端点小于等于当前的
的线段的右端点。即线段里有可能有
的线段。
- 如果收纳集合内存在至少
个大于等于
的右端点,那么就认为
必定可行,于是继续向右搜索更大的
。
时间复杂度
#include <bits/stdc++.h>
#define sc(x) scanf("%d", &(x))
#define pr(x) printf("%d\n", (x))
using namespace std;
typedef pair<int, int> pii;
const int N = 3e5 + 7;
#define lowbit(x) ((x) & (-x))
int tree[N];
inline void add(int i) {
for (int pos = i; pos < N; pos += lowbit(pos)) ++tree[pos];
}
inline int query(int n) {
int ans = 0;
for (int pos = n; pos; pos -= lowbit(pos)) ans += tree[pos];
return ans;
}
pii a[N];
int main() {
int n, m;
sc(n), sc(m);
for (int i = 0; i < m; ++i) sc(a[i].first), sc(a[i].second);
sort(a, a + m);
int ans = 0;
for (int L = 1, p = 0; L <= n; ++L) {
while (p < m && a[p].first <= L) add(a[p++].second);
int left = L, right = n, R = 0;
while (left <= right) {
int mid = left + right >> 1;
int tot = p - query(mid - 1);
if (tot >= mid - L + 1)
left = mid + 1, R = mid;
else
right = mid - 1;
}
ans = max(ans, R - L + 1);
}
pr(ans);
return 0;
} 大法师思路
- 因为答案必定在
中,所以可以考虑二分
- 将所有长度满足的区间收纳进一个集合,然后对右端点按照从大到小排序
- 枚举所有线段,用BIT维护左端点,如果数量满足要求check就返回1。
时间复杂度
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn = 3e5 + 5;
int n, m;
int tree[maxn];
int lowbit(int x) { return x & (-x); }
void add(int x, int val) {
for (int i = x; i <= n; i += lowbit(i)) tree[i] += val;
}
int Find(int x) {
int res = 0;
for (int i = x; i; i -= lowbit(i)) res += tree[i];
return res;
}
struct node {
int l, r;
int len;
} p[maxn], s[maxn];
int tot = 0;
bool cmp(node a, node b) { return a.r < b.r; }
bool check(int val) {
memset(tree, 0, sizeof(tree));
tot = 0;
for (int i = 1; i <= m; i++) {
if (p[i].len >= val) {
tot++;
s[tot].l = p[i].l, s[tot].r = p[i].r;
add(p[i].l, 1);
}
}
sort(s + 1, s + 1 + tot, cmp);
for (int i = 1; i <= tot; i++) {
int v = Find(s[i].r - val + 1);
add(s[i].l, -1);
if (v >= val) return true;
}
return false;
}
int main() {
cin >> n >> m;
for (int i = 1; i <= m; i++) {
cin >> p[i].l >> p[i].r;
p[i].len = p[i].r - p[i].l + 1;
}
int l = 1, r = m;
while (l < r) {
int mid = (l + r + 1) >> 1;
if (check(mid))
l = mid;
else {
r = mid - 1;
}
}
printf("%d\n", l);
return 0;
} 吴博雄思路
#include <bits/stdc++.h>
#define sc(x) scanf("%d", &(x))
#define pr(x) printf("%d\n", x)
#define L first
#define R second
using namespace std;
const int N = 3e5 + 7;
pair<int, int> seg[N];
int main() {
int n, m;
sc(n), sc(m);
for (int i = 0; i < m; ++i) sc(seg[i].L), sc(seg[i].R);
sort(seg, seg + m); //线段按照左端点从小到大排序
int ans = 0, cnt = 0;
priority_queue<int, vector<int>, greater<int>> q; //小顶堆维护区间右端点
for (int i = 0; i < m; ++i) { //交集的左端点必定是某个区间的左端点
q.push(seg[i].R), ++cnt; //交集的右端点必定是某个区间的右端点
while (cnt > q.top() - seg[i].L + 1) q.pop(), --cnt;
//本行是理解此种做法的重点:cnt即是tot,而前值则是交集长度
//不可行的交集求长度为负值
ans = max(ans, cnt);
}
pr(ans);
return 0;
}
/*
1 3
5 6
5 9
*/ 
京公网安备 11010502036488号