题目大意:在数轴上有$n$个闭区间$[l_1,r_1],[l_2,r_2],...,[l_n,r_n]$。现在要从中选出 $m$ 个区间,使得这 $m$ 个区间共同包含至少一个位置。输出被选中的最长区间长度减去被选中的最短区间长度,若多解,输出最小的一个
题解:把区间按长度排序,然后把左右端点离散化,双指针扫一下,线段树维护一下有没有点被覆盖$ geq m$次即可
卡点:1.因为有$2 imes n$个点,所以线段树开小了一倍
C++ Code:
#include <cstdio> #include <algorithm> #define maxn 500010 using namespace std; const int inf = 0x3f3f3f3f; int n, m, tot, M, ans; int p[maxn << 1]; struct interval { int l, r, len; bool operator < (const interval & b) const {return len < b.len;} } s[maxn]; inline int min(int a, int b) {return a < b ? a : b;} inline int max(int a, int b) {return a > b ? a : b;} int V[maxn << 3], cov[maxn << 3]; void pushdown(int rt) { int &tmp = cov[rt]; V[rt << 1] += tmp; V[rt << 1 | 1] += tmp; cov[rt << 1] += tmp; cov[rt << 1 | 1] += tmp; tmp = 0; } void add(int rt, int l, int r, int L, int R, int num = 1) { if (L <= l && R >= r) { V[rt] += num; cov[rt] += num; return ; } if (cov[rt]) pushdown(rt); int mid = l + r >> 1; if (L <= mid) add(rt << 1, l, mid, L, R, num); if (R > mid) add(rt << 1 | 1, mid + 1, r, L, R, num); V[rt] = max(V[rt << 1], V[rt << 1 | 1]); } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) { scanf("%d%d", &s[i].l, &s[i].r); s[i].len = s[i].r - s[i].l; p[i - 1 << 1 | 1] = s[i].l; p[i << 1] = s[i].r; } tot = n << 1; sort(p + 1, p + tot + 1); sort(s + 1, s + n + 1); M = lower_bound(p + 1, p + tot + 1, p[tot]) - p; for (int i = 1; i <= n; i++) { s[i].l = lower_bound(p + 1, p + tot + 1, s[i].l) - p; s[i].r = lower_bound(p + 1, p + tot + 1, s[i].r) - p; } int L = 1; ans = inf; for (int i = 1; i <= n; i++) { add(1, 1, M, s[i].l, s[i].r); while (V[1] >= m && L <= i) { ans = min(ans, s[i].len - s[L].len); add(1, 1, M, s[L].l, s[L].r, -1); L++; } } printf("%d ", (ans == inf) ? -1 : ans); return 0; }