APIO 2012 守卫 | 差分 / 线段树 + 贪心

题目:luogu 3634

首先把为 0 的区间删去,重新标号,可以差分也可以线段树。

把包含其他线段的线段删去,原因 1 是它没有用,原因 2 下面再说。然后,贪心选取最少的点来满足所有线段,即选取还没有点在上面的线段的右端点。如下图中选取的红色方格。

倘若不删去包含其他线段的线段,如上图中的蓝色虚线,我们在贪心选取点的时候,就会先扫到蓝线的左端点而后扫到第二条红线,按照规则,我们会选择蓝线的右端点 6 号点,接下来扫到第二条红线时,由于它上面并没有点被选取,所以又会选取它的右端点 5 号点,这样显然不是最优的,这就是第 2 个原因。

选完点后,从左往右扫,每扫到一个选取的点,如果该点不是必选的(覆盖它的线段 l ≠ r),就尝试若不选它是否可行,即选取它左边相邻的点,计算出选取这个点时的最少点数,计算方法如下。

如上图,用 F[ i ] 表示前 i 条线段需要选取的最少点数,G[ i ] 表示后 i 条线段需要选取的最少点数,假设现在我们尝试不选 5 号点,计算选取 4 号点时需要选取的最少点数,然后与 k 比较,方法是二分找出右端点最大且小于 4 的线段 x,找出左端点最小且大于 4 的线段 y,若 F[ x ] + G[ y ] + 1 大于 k 则尝试失败,说明先前点是必选的。

 1 #include <cstdio>
 2 #include <string>
 3 #include <algorithm>
 4
 5 const int N = 100005;
 6
 7 struct line {
 8     int l, r, f;
 9     bool operator < (const line &cmp) const {
10         return l < cmp.l;
11     }
12 } a[N];
13
14 int b[N], L[N], R[N], be[N], F[N], G[N];
15
16 int read() {
17     int x = 0, f = 1;
18     char c = getchar();
19     while (!isdigit(c)) {
20         if (c == ‘-‘) f = -1;
21         c = getchar();
22     }
23     while (isdigit(c)) {
24         x = (x << 3) + (x << 1) + (c ^ 48);
25         c = getchar();
26     }
27     return x * f;
28 }
29
30 int main() {
31     int n = read(), k = read(), m = read();
32     for (int i = 1; i <= m; ++ i) {        //差分标记为 0 的区间
33         a[i].l = read(), a[i].r = read(), a[i].f = read();
34         if (a[i].f == 0) ++b[a[i].l], --b[a[i].r+1];
35     }
36     int cur = 0, cnt = 0;
37     for (int i = 1; i <= n; ++ i) {        //去除为 0 的区间并重新标号
38         cur += b[i];
39         if (cur == 0) L[i] = R[i] = ++cnt, be[cnt] = i;
40     }
41     if (cnt == k) {                        //恰好满足
42         for (int i = 1; i <= cnt; ++ i) printf("%d\n", be[i]);
43         return 0;
44     }
45     L[n + 1] = n + 1;
46     for (int i = 1; i <= n; ++ i)
47         if (R[i] == 0) R[i] = R[i - 1];
48     for (int i = n; i >= 1; -- i)
49         if (L[i] == 0) L[i] = L[i + 1];
50     cnt = 0;
51     for (int i = 1; i <= m; ++ i) {
52         if (a[i].f == 0) continue;
53         int l = L[a[i].l], r = R[a[i].r];
54         if (l <= r) a[++cnt].l = l, a[cnt].r = r;
55     }
56     std::sort(a + 1, a + cnt + 1);
57     int top = 0;
58     for (int i = 1; i <= cnt; ++ i) {    //去除包含其他线段的线段
59         while (top && a[i].l >= L[top] && a[i].r <= R[top]) --top;
60         L[++top] = a[i].l, R[top] = a[i].r;
61     }
62     int l = n + 1, r = 0;
63     for (int i = 1; i <= top; ++ i) {    //贪心选取最少的点
64         if (L[i] > r) F[i] = F[i - 1] + 1, r = R[i];
65         else F[i] = F[i - 1];
66     }
67     for (int i = top; i >= 1; -- i) {
68         if (R[i] < l) G[i] = G[i + 1] + 1, l = L[i];
69         else G[i] = G[i + 1];
70     }
71     bool ok = 0;
72     for (int i = 1; i <= top; ++ i) {    //尝试不选
73         if (F[i] == F[i - 1]) continue;
74         if (L[i] == R[i]) {
75             printf("%d\n", be[R[i]]);
76             ok = 1; continue;
77         }
78         int l = 1, r = i - 1, x = 0, y = top + 1;
79         while (l <= r) {                //二分查找
80             int mid = l + ((r - l) >> 1);
81             if (R[mid] < R[i] - 1) x = mid, l = mid + 1;
82             else r = mid - 1;
83         }
84         l = i + 1, r = top;
85         while (l <= r) {
86             int mid = l + ((r - l) >> 1);
87             if (L[mid] > R[i] - 1) y = mid, r = mid - 1;
88             else l = mid + 1;
89         }
90         if (F[x] + G[y] + 1 > k) {
91             printf("%d\n", be[R[i]]);
92             ok = 1;
93         }
94     }
95     if (!ok) puts("-1");
96     return 0;
97 }

原文地址:https://www.cnblogs.com/milky-w/p/8525350.html

时间: 2024-10-12 22:15:27

APIO 2012 守卫 | 差分 / 线段树 + 贪心的相关文章

[Codeforces 1295E]Permutation Separation(线段树+贪心)

[Codeforces 1295E]Permutation Separation(线段树+贪心) 题面 给出一个排列\(p_1,p_2,...p_n\).初始时你需要选择一个位置把排列分成左右两个.然后在两个序列间移动元素使得左边序列的所有元素都比右边的所有元素小.给出每个元素\(p_i\)从一个序列移动到另一个序列的代价\(a_i\). 分析 显然最后得到的序列是小的数在一边,大的数在另一边.设从值为\(i\)的元素处分开之后移动代价为\(ans_i\). 一开始假设所有数都移到右边序列,那么

【Luogu】P1607庙会班车Fair Shuttle(线段树+贪心)

我不会做贪心题啊--贪心题啊--题啊--啊-- 我真TM菜爆了啊-- 这题就像凌乱的yyy一样,把终点排序,终点相同的按起点排序.然后维护一个查询最大值的线段树.对于一个区间[l,r],如果这个区间已经有的最大值为s,那么这个区间最多还能装下c-s头奶牛. 当然奶牛数量没那么多的话我也是没有办法 最后说一句,奶牛到终点就下车了,可以给别的奶牛腾空间,不计入个数.所以奶牛在车上的区间为[l,r-1]. #include <cstdio> #include <iostream> #in

[LuoguP1438]无聊的数列(差分+线段树/树状数组)

\(Link\) \(\mathcal{Description}\) 给你一个数列,要求支持单点查询\(and\)区间加等差数列. \(\mathcal{Solution}\) 哈哈哈哈这个题十分的有意思,至于为什么有意思等会儿再说~ 其实我们观察这两个操作,单点查询--就是那个\(naive\)的单点查询,那么区间加等差数列呢?我们可以思考一下等差数列的性质--存在公差.不妨考虑差分 \(emmm\)发现我好像还没有在博客园里提过差分--那么就整一整吧正好我好久没捯饬这玩意儿了\(qwq\)

HDU 2795 Billboard (线段树+贪心)

手动博客搬家:本文发表于20170822 21:30:17, 原地址https://blog.csdn.net/suncongbo/article/details/77488127 URL: http://acm.hdu.edu.cn/showproblem.php?pid=2795题目大意:有一个h*w的木板 (h, w<=1e9), 现在有n (n<=2e5)张1*xi的海报要贴在木板上,按1~n的顺序每次贴海报时会选择最上的一排的最左边贴 (海报不能互相覆盖), 求每张海报会被贴在哪一行

[树上差分][线段树合并]JZOJ 3397 雨天的尾巴

Description 深绘里一直很讨厌雨天. 灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切. 虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连 根拔起,以及田地里的粮食被弄得一片狼藉. 无奈的深绘里和村民们只好等待救济粮来维生. 不过救济粮的发放方式很特别. 首先村落里的一共有n 座房屋,并形成一个树状结构.然后救济粮分m 次发放,每次选择 两个房屋(x,y),然后对于x 到y 的路径上(含x 和y) 每座房子里发放一袋z 类型的救济粮.

G - Greg and Array CodeForces - 296C 差分+线段树

题目大意:输入n,m,k.n个数,m个区间更新标记为1~m.n次操作,每次操作有两个数x,y表示执行第x~y个区间更新. 题解:通过差分来表示某个区间更新操作执行的次数.然后用线段树来更新区间. #include<bits/stdc++.h> using namespace std; const int N=1E5+7; typedef long long ll; ll arr[N]; ll tt[N],cnt[N]; struct stu{ ll value,add; }tree[N+N+N

HDU 5638 Toposort 线段树+贪心

题意:bc round 74 分析: 参考下普通的用堆维护求字典序最小拓扑序, 用某种数据结构维护入度小于等于k的所有点, 每次找出编号最小的, 并相应的减少k即可. 这个数据结构可以用线段树, 建立一个线段树每个节点[l,r]维护编号从ll到rr的所有节点的最小入度, 查询的时候只需要在线段树上二分, 找到最小的x满足入度小于等于k. 复杂度O((n+m)logn) #include <iostream> #include <cstdio> #include <vector

【HDU】5195-DZY Loves Topological Sorting(拓扑 + 线段树 + 贪心)

每次找出入度小于K的编号最大点. 找的时候用线段树找,找完之后将这个点出度链接的点的入度全部减一 简直爆炸... #include<cstdio> #include<vector> #include<cstring> #include<algorithm> using namespace std; #define lson (pos<<1) #define rson (pos<<1|1) const int maxn = 100005

【BZOJ-3252】攻略 DFS序 + 线段树 + 贪心

3252: 攻略 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 339  Solved: 130[Submit][Status][Discuss] Description 题目简述:树版[k取方格数] 众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏. 今天他得到了一款新游戏<XX半岛>,这款游戏有n个场景(scene),某些场景可以通过不同的选择支到达其他场景.所有场景和选择支构成树状结构:开始游戏时在根节点(共通线)