1 条题解
-
0
Guest MOD
-
1
离散化
俗话说的好,遇到一道题,先看一下数据范围,
看看能不能骗分(其实骗分是一种技能),l、r都<=1e9很显然是要离散化了。离散化模板:
#include <algorithm> uing namespace std; for (int i = 1; i <= n; i++) b[i] = a[i]; sort(b + 1, b + n + 1); // 先排序 // 因为 unique(b + 1, b + n + 1) 是返回 b + n + 1,所以要 - b - 1 tot = unique(b + 1, b + n + 1) - b - 1; // 再去重 for (int i = 1; i <= n; i++) { /* *lower_bound(b + 1, b + tot + 1, a[i]) 是在 b 中找到第一个 >= a[i] 的数指针 *因为 b 中是绝对包含 a[i] 的(b[i] = a[i];)且 b 数组去重了 *所以lower_bound(b + 1, b + tot + 1, a[i]) 是在 b 中找到值为 a[i] 指针 *又因为是返回指针,所以要 - b */ a[i] = lower_bound(b + 1, b + tot + 1, a[i]) - b; }不会 lower_bound 可以先看最后
错误写法
p[i].l = lower_bound(uni + 1, uni + tot + 1, p[i].l) - (uni + 1); p[i].r = lower_bound(uni + 1, uni + tot + 1, p[i].r) - (uni + 1);正确写法
p[i].l = lower_bound(uni + 1, uni + tot + 1, p[i].l) - uni; p[i].r = lower_bound(uni + 1, uni + tot + 1, p[i].r) - uni;思路
在看题,“选中的最长区间长度减去被选中的最短区间长度”,题目要求的答案跟“最长”和“最短”有关,所以我们就会联想到单调性。
我们先思考一种较暴力的做法,那就是按排序后的顺序逐一加入区间,然后看看是否有一个点的被覆盖次数>=m。
如果有的话那就取
min{ans, len[i] - len[last]}其中len = r - l,然后将前面加入的按顺序删掉,直到<m。显然这利用了#&……¥&*%…………@#!(尺取法)的思想。
现在问题就是我们如何快速地得知是否有一个点的被覆盖次数>=m。
那就很显然维护一棵线段树就好了。
细节
线段树模板见 P3372
不知道有没有人query写复杂了,就是
tr[1].max鸭,因为每次就是全部最大值记得判-1鸭(没逝反正测试点会告诉你一切)
然后呢?然后就AC了。
代码
// 区间 // P1712 #include <cstdio> #include <algorithm> #define lc u << 1 #define rc u << 1 | 1 #define inf 0x3f3f3f3f using namespace std; const int N = 5e5 + 114514; // 预防线段树子结点越界 int n, m, d[N << 1], tot; struct Node { int l, r, len; }p[N]; struct Tree { int l, r; int val, tag; }tre[N << 2]; // 线段树开4倍数组 bool cmp(Node x, Node y) { return x.len < y.len; } // 离散化排序的比较函数 inline void pushup(int u) { tre[u].val = max(tre[lc].val, tre[rc].val); } void pushdown(int u) { if (tre[u].tag) { tre[lc].val += tre[u].tag; tre[rc].val += tre[u].tag; tre[lc].tag += tre[u].tag; tre[rc].tag += tre[u].tag; tre[u].tag = 0; } } void build(int u, int l, int r) { tre[u] = {l, r, 0, 0}; if (l == r) return; int mid = l + r >> 1; build(lc, l, mid); build(rc, mid + 1, r); pushup(u); } void change(int u, int l, int r, int v) { if (l <= tre[u].l && tre[u].r <= r) { tre[u].val += v; tre[u].tag += v; return; } int mid = tre[u].l + tre[u].r >> 1; pushdown(u); if (l <= mid) change(lc, l, r, v); if (r > mid) change(rc, l, r, v); pushup(u); } int main() { scanf("%d %d", &n, &m); for (int i = 1; i <= n; i++) { scanf("%d %d", &p[i].l, &p[i].r); p[i].len = p[i].l - p[i].r; d[++tot] = p[i].l, d[++tot] = p[i].r; // 离散化数组 } sort(p + 1, p + n + 1, cmp); // 先排序 sort(d + 1, d + tot + 1); // 再去重 tot = unique(d + 1, d + tot + 1) - (d + 1); for (int i = 1; i <= n; i++) { // 二分找到p[i].l 或 p[i].r p[i].l = lower_bound(d + 1, d + tot + 1, p[i].l) - d; p[i].r = lower_bound(d + 1, d + tot + 1, p[i].r) - d; } int last = 1, ans = inf; build(1, 1, tot); // 尺取法求最小花费 for (int i = 1; i <= n; i++) { change(1, p[i].l, p[i].r, 1); while (tre[1].val >= m) { change(1, p[last].l, p[last].r, -1); ans = min(ans, p[i].len - p[last].len); last++; } } if (ans == inf) puts("-1"); else printf("%d\n", ans); return 0; }完了吗? NO
lower_bound
首先 lower_bound 在
#include <algorithm>中,且要using namespace std;lower_bound 有三个参数我们举个栗子🌰
int a[9] = {1, 4, 6, 2 ,7, 3, 0, 9, 5, 3}; // 开了一个长度为9的数组 // lower_bound(a, a + 10, 2) 返回 a + 4,lower_bound(a, a + 10, 2) - a 返回 4 cout << lower_bound(a, a + 10, 2) - a << '\n'; // lower_bound(a, a + 10, 2) 返回 a + 8,lower_bound(a, a + 10, 8) - a 返回 8 cout << lower_bound(a, a + 10, 8) - a << '\n';所以 lower_bound 返回第一个大于等于x的数的指针
再升级一下
int n, a[100]; // 开了一个长度为100的数组 cin >> n; for (int i = 1; i <= n; i++) cin >> a[i]; // 在 a + 1 ~ a + n + 1 中找到第一个大于等于2的数的数的下标 cout << lower_bound(a + 1, a + n + 1, 2) - a << '\n'; // 在 a + 1 ~ a + n + 1 中找到第一个大于等于8的数的数的下标 cout << lower_bound(a + 1, a + n + 1, 8) - a << '\n';是在看不懂在 WZH 那学 动态开点
虽然更看不懂推荐给
tiangeyuan2025硬币学习更好的线段树
- 1
信息
- ID
- 2587
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 4
- 标签
- 递交数
- 1
- 已通过
- 0
- 上传者