【luogu P2839】middle

middle

题目链接:luogu P2839

题目大意

有一个数组,给你左区间和右区间的范围,问你构成的子区间中最大的中位数是多少。

思路

这道题你想想中位数是干嘛的。

那假设中位数是 x,我们把 <x 的看做是 1,否则看做是 1,那这些数的这个值的和应该是 0/1
那如果小于 0,就是不能达到这个中位数的大小,大于 1 就是超过了这个中位数的大小。

那我们可以以此来二分一个答案。
那我们看看么看和是多少,毕竟你不能直接扫过去。

那你看,它左右区间其实可以分成三段。中间那一段是一定要选的:b+1c1,左边选最大后缀(至少选一个):ab,右边选最大前缀(至少选一个):cd

那这个其实都可以用线段树来维护。
但是你发现你选择的中位数一旦改变,就要重新建一棵树。
那你考虑中位数先按顺序看,到下一个之后,你这个值的值就由 1 变成了 1,也只有这个改变了。

那你会想到主席树,然后就好了。

代码

#include<cstdio> #include<algorithm> using namespace std; struct Tree { int l, r, x, sum, lmax, rmax; }tree[20001 << 5]; struct node { int x, num; }numm[20001]; int n, last_answer, middle, now_sum; int q, a, b, c, d, lef, rig; int root[20001], t_n, answer; bool cmp(node x, node y) { return x.x < y.x; } int clone(int now) {//主席树 t_n++; tree[t_n] = tree[now]; return t_n; } int build(int now, int l, int r) { now = ++t_n; if (l == r) { tree[now].x = 1; tree[now].sum = tree[now].x; tree[now].lmax = tree[now].rmax = tree[now].x; return now; } int mid = (l + r) >> 1; tree[now].l = build(tree[now].l, l, mid); tree[now].r = build(tree[now].r, mid + 1, r); //这里主席树还要多记录几个值:区间和,区间前缀最大值,区间后缀最大值 tree[now].sum = tree[tree[now].l].sum + tree[tree[now].r].sum; tree[now].lmax = max(tree[tree[now].l].lmax, tree[tree[now].l].sum + tree[tree[now].r].lmax); tree[now].rmax = max(tree[tree[now].r].rmax, tree[tree[now].r].sum + tree[tree[now].l].rmax); return now; } int change(int now, int l, int r, int loc, int x) { now = clone(now); if (l == r) { tree[now].x = tree[now].sum = x; tree[now].lmax = tree[now].rmax = tree[now].x; return now; } int mid = (l + r) >> 1; if (loc <= mid) tree[now].l = change(tree[now].l, l, mid, loc, x); else tree[now].r = change(tree[now].r, mid + 1, r, loc, x); //上面说的记录的值的维护 tree[now].sum = tree[tree[now].l].sum + tree[tree[now].r].sum; tree[now].lmax = max(tree[tree[now].l].lmax, tree[tree[now].l].sum + tree[tree[now].r].lmax); tree[now].rmax = max(tree[tree[now].r].rmax, tree[tree[now].r].sum + tree[tree[now].l].rmax); return now; } int get_sum(int now, int l, int r, int L, int R) {//区间和 if (l >= L && r <= R) { return tree[now].sum; } int mid = (l + r) >> 1, re = 0; if (L <= mid) re += get_sum(tree[now].l, l, mid, L, R); if (R >= mid + 1) re += get_sum(tree[now].r, mid + 1, r, L, R); return re; } int get_lef(int now, int l, int r, int L, int R) {//区间前缀最大值 if (l >= L && r <= R) return tree[now].lmax; int mid = (l + r) >> 1; if (mid + 1 <= R && mid >= L) return max(get_lef(tree[now].l, l, mid, L, R), get_sum(tree[now].l, l, mid, L, R) + get_lef(tree[now].r, mid + 1, r, L, R)); else if(mid + 1 <= R) return get_lef(tree[now].r, mid + 1, r, L, R); else if (mid >= L) return get_lef(tree[now].l, l, mid, L, R); } int get_rig(int now, int l, int r, int L, int R) {//区间后缀最大值 if (l >= L && r <= R) return tree[now].rmax; int mid = (l + r) >> 1; if (mid + 1 <= R && mid >= L) return max(get_rig(tree[now].r, mid + 1, r, L, R), get_sum(tree[now].r, mid + 1, r, L, R) + get_rig(tree[now].l, l, mid, L, R)); else if(mid + 1 <= R) return get_rig(tree[now].r, mid + 1, r, L, R); else if (mid >= L) return get_rig(tree[now].l, l, mid, L, R); } bool check(int middle) { now_sum = 0;//分成三段,必须要选,左边选一段后缀,右边选一段前缀 if (b + 1 <= c - 1) now_sum += get_sum(root[middle], 1, n, b + 1, c - 1); now_sum += get_rig(root[middle], 1, n, a, b); now_sum += get_lef(root[middle], 1, n, c, d); return now_sum >= 0; } void sort_abcd() {//把数排序(一开始没弄数组,后来发现要排序之后懒得改,就直接这样了) if (a > b) swap(a, b); if (a > c) swap(a, c); if (a > d) swap(a, d); if (b > c) swap(b, c); if (b > d) swap(b, d); if (c > d) swap(c, d); } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &numm[i].x); numm[i].num = i; } sort(numm + 1, numm + n + 1, cmp); root[1] = build(0, 1, n);//按大小插 for (int i = 2; i <= n; i++) root[i] = change(root[i - 1], 1, n, numm[i - 1].num, -1); scanf("%d", &q); for (int times = 1; times <= q; times++) { scanf("%d %d %d %d", &a, &b, &c, &d); a = (a + last_answer) % n;//求出输入 b = (b + last_answer) % n; c = (c + last_answer) % n; d = (d + last_answer) % n; a++; b++; c++; d++; sort_abcd(); lef = 1; rig = n; while (lef <= rig) {//二分 middle = (lef + rig) >> 1; if (check(middle)) { answer = middle; lef = middle + 1; } else rig = middle - 1; } last_answer = numm[answer].x; printf("%d\n", last_answer); } return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/luogu_P2839.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(94)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示