【YBT2023寒假Day12 B】仰望星空(DP)(线段树)(笛卡尔树)

仰望星空

题目链接:YBT2023寒假Day12 B

题目大意

有一个 n*n 的网格,第 i 列下面的 ai 个点都是障碍。
然后又一些不是障碍的地方有特殊点,删掉它有费用。
要你用最小费用使得不存在两个特殊点在一个矩阵中且矩阵中没有障碍。

思路

注意到障碍很特殊,考虑从这里开始思考。
会发现两个特殊点可以同时存在的条件是它们所在的列形成的区间 ai 的最大值要大于行小的那个。

那你可以考虑保留那些点,使得每对都是这样的,而且保留下的费用最大。
那这个最大值不难想到笛卡尔树。
考虑对于笛卡尔树上的一个点,它的区间是 lr,那个最大值是 x,它父亲的最大值是 y(如果没有父亲那 y=N)。
那对于 [lr][x+1,y] 是一个空的,这里可以放一个点。

那考虑放或者不放。
不放的话,那问题就直接变成两个子树的答案和。
放的话,那如果它放在了第 i 列,那我们一直到属于第 i 列的最底下的点,路径上的所有点都不能放了。
那我们就要把剩下的子树的 DP 值加起来得到答案。

那至于怎么加,我们可以发现要加的子树是这条链上每个点的另一个兄弟的答案。
那我们除了 fx 表示 i 子树的答案,再设一个 fx 是它兄弟的答案,那我们用一个线段树,每次给 x 代表的区间加上贡献,询问就是单点查询了(所以树状数组也行)。

代码

#include<set> #include<cstdio> #include<algorithm> #define ll long long using namespace std; const int N = 2e5 + 100; struct node { int x, y, c; }b[N]; int n, a[N], m, id[N]; set <int> s; ll ans; bool cmpa(int x, int y) { return a[x] < a[y]; } bool cmpb(node x, node y) { if (x.y != y.y) return x.y < y.y; return x.x < y.x; } struct XD_tree { ll f[N << 2]; void update(int now, int l, int r, int L, int R, ll x) { if (L > R) return ; if (L <= l && r <= R) { f[now] += x; return ; } int mid = (l + r) >> 1; if (L <= mid) update(now << 1, l, mid, L, R, x); if (mid < R) update(now << 1 | 1, mid + 1, r, L, R, x); } ll query(int now, int l, int r, int pl) { if (l == r) return f[now]; int mid = (l + r) >> 1; if (pl <= mid) return f[now] + query(now << 1, l, mid, pl); else return f[now] + query(now << 1 | 1, mid + 1, r, pl); } }T; int main() { freopen("starry.in", "r", stdin); freopen("starry.out", "w", stdout); scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &a[i]), id[i] = i; sort(id + 1, id + n + 1, cmpa); scanf("%d", &m); for (int i = 1; i <= m; i++) { scanf("%d %d %d", &b[i].x, &b[i].y, &b[i].c); } sort(b + 1, b + m + 1, cmpb); for (int i = 0; i <= n + 1; i++) s.insert(i); int L = 1; for (int i = 1; i <= m; i++) { while (L <= n && a[id[L]] < b[i].y) s.erase(s.find(id[L])), L++; ll no = b[i].c, yes = T.query(1, 1, n, b[i].x); if (no < yes) { ans += no; } else { ans += yes; set <int> ::iterator it = s.lower_bound(b[i].x); int r = (*it) - 1, l = (*(--it)) + 1; T.update(1, 1, n, l, r, -yes + b[i].c);//撤销选的操作并补回不选的费用 } } printf("%lld", ans); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/YBT2023Day12_B.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
历史上的今天:
2022-02-22 【luogu P6242】【模板】线段树 3(吉司机线段树)
2022-02-22 【NOI2021模拟测试赛(四十二)】D(cdq分治)(树状数组)
2022-02-22 【NOI2021模拟测试赛(四十二)】chemistry(珂朵莉树)(动态开点线段树)
2021-02-22 【luogu P3690】【模板】Link Cut Tree (动态树)
2021-02-22 【luogu P3384】【模板】轻重链剖分
2021-02-22 【ybt金牌导航5-1-1】【luogu P2590】树的统计
2021-02-22 【ybt金牌导航4-5-1】【luogu P3369】普通平衡树(替罪羊树做法)
点击右上角即可分享
微信分享提示