【YBT2023寒假Day7 C】查区间(线段树套线段树)

查区间

题目链接:YBT2023寒假Day7 C

题目大意

给你一个序列,要你维护两种操作,区间取 min 和区间求第 k 小。

思路

关于区间求第 k 小,有一个方法,是树套树。
外层线段树维护位置,内层线段树是值域线段树维护每个数的出现次数。
就可以做到 O(nlog2n) 维护。

考虑区间取 min 怎么搞。
发现你定位到每个线段树上的区间,有两个东西要考虑:
祖先的位置你要搞,子树的位置你也要改。

不过我们先看自己怎么改,就是可以把大于的所有数找出来,一个一个加进去(这样搞是因为易于操作,容易扩展)。
这样的复杂度是没问题的因为每次取 min 不会使得总数的种类增加,而且你找了 x 个,种类就少了 x1/x(可能放进去的值已经有了),那这样复杂度是 O(n) 的。

首先看简单的子树,那一个想法是懒标记,会发现确实可行,你就每个儿子都像你一样操作一下就好了。
接着是祖先,因为是线段树,所以至多 log 个祖先,所以我们也可以尝试暴力改。
不过要注意的是你要用你一开始的区间里面找到的种类和数量,因为祖先区间里面有一些 >x 的不能被取 min
那这个复杂度就是 nlog3n 了。

(好像说是有一个 nn 的分块套分块,外层是按位置分块,内层按值域分块,具体操作阿巴阿巴不会捏)

代码

#include<cstdio> #include<vector> #include<iostream> #include<algorithm> using namespace std; const int N = 8e4 + 100; int n, m, a[N], b[N]; vector <pair<int, int> > tmp; vector <int> Tmp; struct Make_XD_tree { int tot, f[N << 9], ls[N << 9], rs[N << 9]; int new_point() { return ++tot; } void up(int now) { f[now] = f[ls[now]] + f[rs[now]]; } void add(int &now, int l, int r, int pl, int x) { if (!now) now = new_point(); if (l == r) { f[now] += x; return ; } int mid = (l + r) >> 1; if (pl <= mid) add(ls[now], l, mid, pl, x); else add(rs[now], mid + 1, r, pl, x); up(now); } vector <int> Left(vector <int> now) { vector <int> op; op.clear(); for (int i = 0; i < now.size(); i++) if (ls[now[i]]) op.push_back(ls[now[i]]); return op; } vector <int> Right(vector <int> now) { vector <int> op; op.clear(); for (int i = 0; i < now.size(); i++) if (rs[now[i]]) op.push_back(rs[now[i]]); return op; } int getk(vector <int> now, int l, int r, int k) { if (l == r) return l; int mid = (l + r) >> 1, cnt = 0; for (int i = 0; i < now.size(); i++) if (ls[now[i]]) cnt += f[ls[now[i]]]; if (cnt >= k) return getk(Left(now), l, mid, k); else return getk(Right(now), mid + 1, r, k - cnt); } void ask(int now, int l, int r, int L, int R) { if (L > R) return ; if (!f[now]) return ; if (!now) return ; if (l == r) { tmp.push_back(make_pair(l, f[now])); return ; } int mid = (l + r) >> 1; if (L <= mid) ask(ls[now], l, mid, L, R); if (mid < R) ask(rs[now], mid + 1, r, L, R); } }Tr; struct XD_Tree { int lzy[N << 3], rt[N << 3]; void build(int now, int l, int r) { lzy[now] = 2000000000; for (int i = l; i <= r; i++) { Tr.add(rt[now], 1, n, a[i], 1); } if (l == r) return ; int mid = (l + r) >> 1; build(now << 1, l, mid); build(now << 1 | 1, mid + 1, r); } void downc(int now, int x) { lzy[now] = min(lzy[now], x); tmp.clear(); Tr.ask(rt[now], 1, n, x + 1, n); int cnt = 0; for (int i = 0; i < tmp.size(); i++) { Tr.add(rt[now], 1, n, tmp[i].first, -tmp[i].second); cnt += tmp[i].second; } Tr.add(rt[now], 1, n, x, cnt); } void down(int now) { if (lzy[now] != 2000000000) { downc(now << 1, lzy[now]); downc(now << 1 | 1, lzy[now]); lzy[now] = 2000000000; } } void ask(int now, int l, int r, int L, int R) { if (L <= l && r <= R) { Tmp.push_back(rt[now]); return ; } int mid = (l + r) >> 1; down(now); if (L <= mid) ask(now << 1, l, mid, L, R); if (mid < R) ask(now << 1 | 1, mid + 1, r, L, R); } void update(int now, int l, int r, int L, int R, int x) { if (L <= l && r <= R) { lzy[now] = min(lzy[now], x); tmp.clear(); Tr.ask(rt[now], 1, n, x + 1, n); int Now = now, cnt = 0; for (int i = 0; i < tmp.size(); i++) cnt += tmp[i].second; while (Now) { for (int i = 0; i < tmp.size(); i++) Tr.add(rt[Now], 1, n, tmp[i].first, -tmp[i].second); Tr.add(rt[Now], 1, n, x, cnt); Now >>= 1; } return ; } int mid = (l + r) >> 1; down(now); if (L <= mid) update(now << 1, l, mid, L, R, x); if (mid < R) update(now << 1 | 1, mid + 1, r, L, R, x); } }T; int re; char c; int read() { c = getchar(); re = 0; while (c < '0' || c > '9') c = getchar(); while (c >= '0' && c <= '9') { re = (re << 3) + (re << 1) + c - '0'; c = getchar(); } return re; } int main() { freopen("segment.in", "r", stdin); freopen("segment.out", "w", stdout); n = read(); m = read(); for (int i = 1; i <= n; i++) a[i] = read(); T.build(1, 1, n); for (int i = 1; i <= m; i++) { int op = read(); if (op == 1) { int l = read(), r = read(), x = read(); if (x < n) T.update(1, 1, n, l, r, x); } if (op == 2) { int l = read(), r = read(), x = read(); Tmp.clear(); T.ask(1, 1, n, l, r); printf("%d\n", Tr.getk(Tmp, 1, n, x)); } } return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/YBT2023Day7_C.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
历史上的今天:
2022-02-07 【YBT2022寒假Day2 B】【luogu CF809D】模糊序列 / Hitchhiking in the Baltic States(平衡树优化DP)(fhq-Treap)
2022-02-07 【YBT2022寒假Day2 A】期望旅行(Dij)(期望DP)
点击右上角即可分享
微信分享提示