洛谷P2839 middle 题解
题面
传送门
给定一个长度为 的序列 以及 个询问,每个询问为:求 的左端点在 ,右端点在 的子区间中,最大的中位数。
,。保证 。强制在线。
题解
很好的主席树题目,建模巧妙不套路,真正起到了数据结构维护信息的作用。
关于中位数,有一种较为套路的方法。选一个数,将大于此数的数变为 ,小于此数的数变为 ,相加。若结果非负,则此数大于等于中位数。此题中可以以此二分答案,根据是否存在子区间使和大于等于 判断 的移动。
发现 必须取。然后剩余部分用一个小贪心转化为前/后缀和,则 函数可以写成:将大于等于 的数变为1,否则变为-1,求 的和 + 的最大后缀和 + 的最大前缀和是否非负。
区间的最大前后缀容易想到线段树。但如上方法要对所有 都开一棵线段树,显然不可行。
但又注意到, 加 时,线段树上仅需单点修改(其实并不完全是单点修改,但离散化后 从 到 仅会单点修改 次,均摊到每次增加可看做单点修改)。于是想到主席树。
至此,此题的思路已经很清晰了。先离散化,再对每个值开一棵线段树维护原序列区间,且要用主席树。之后就是 上述 以及线段树常规操作了。时间复杂度 ,空间复杂度 。
Code
此代码在洛谷上奇怪地错误了。后7个点MLE,唯一可能导致炸空间的只有vector,但其空间仅为 ……
BZOJ可以AC。
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 2e4 + 5, M = 8e5 + 5;
int n, a[N], mp[N], m, idx, root[N], q, s[4];
struct Node {int lc, rc, sum, ls, rs;} t[M];
vector<int> c[N];
int read() {
int x = 0, f = 1; char c = getchar();
while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') {x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}
return x * f;
}
void Build(int l, int r, int p) {
t[p].sum = t[p].ls = t[p].rs = r - l + 1;
if (l == r) return ;
int mid = l + r >> 1;
Build(l, mid, t[p].lc = ++idx); Build(mid + 1, r, t[p].rc = ++idx);
}
void pushup(int p) {
Node &u = t[p], l = t[t[p].lc], r = t[t[p].rc];
u.sum = l.sum + r.sum;
u.ls = max(l.ls, l.sum + r.ls);
u.rs = max(r.rs, r.sum + l.rs);
}
void Insert(int l, int r, int x, int ex, int p) {
t[p] = t[ex];
if (l == r) {
t[p].sum = t[p].ls = t[p].rs = -1; return ;
}
int mid = l + r >> 1, f1 = 0, f2 = 0;
for (int i = 0; i < c[x].size(); i++)
if (l <= c[x][i] && c[x][i] <= mid) f1 |= 1;
else if (mid + 1 <= c[x][i] && c[x][i] <= r) f2 |= 1;
if (f1) Insert(l, mid, x, t[ex].lc, t[p].lc = ++idx);
if (f2) Insert(mid + 1, r, x, t[ex].rc, t[p].rc = ++idx);
pushup(p);
}
int Query(int l, int r, int L, int R, int k, int p) {
if (L <= l && r <= R) {
if (k == 1) return t[p].sum;
else if (k == 2) return t[p].rs;
return t[p].ls;
}
int mid = l + r >> 1, f = (L <= mid) + 2 * (R > mid);
if (k == 1) {
if (f == 1) return Query(l, mid, L, R, 1, t[p].lc);
else if (f == 2) return Query(mid + 1, r, L, R, 1, t[p].rc);
return Query(l, mid, L, R, 1, t[p].lc) + Query(mid + 1, r, L, R, 1, t[p].rc);
}
if (f == 1) return Query(l, mid, L, R, k, t[p].lc);
else if (f == 2) return Query(mid + 1, r, L, R, k, t[p].rc);
else {
if (k == 2) return max(Query(mid + 1, r, L, R, 2, t[p].rc), Query(l, mid, L, R, 2, t[p].lc) + Query(mid + 1, r, L, R, 1, t[p].rc));
return max(Query(l, mid, L, R, 3, t[p].lc), Query(mid + 1, r, L, R, 3, t[p].rc) + Query(l, mid, L, R, 1, t[p].lc));
}
}
bool check(int x) {
int res = 0;
if (s[1] < s[2] - 1) res += Query(1, m, s[1] + 1, s[2] - 1, 1, root[x]);
res += Query(1, m, s[0], s[1], 2, root[x]) + Query(1, m, s[2], s[3], 3, root[x]);
return res >= 0;
}
int main() {
int ans = 0;
n = read();
for (int i = 1; i <= n; i++) mp[i] = a[i] = read();
sort(mp + 1, mp + n + 1);
m = unique(mp + 1, mp + n + 1) - (mp + 1);
for (int i = 1; i <= n; i++) c[lower_bound(mp + 1, mp + m + 1, a[i]) - mp].push_back(i);
Build(1, m, root[1] = ++idx);
for (int i = 2; i <= m; i++) Insert(1, m, i - 1, root[i - 1], root[i] = ++idx);
q = read();
while (q--) {
for (int i = 0; i < 4; i++) s[i] = (read() + ans) % n + 1;
sort(s, s + 4);
int l = 1, r = m, mid;
while (l < r) {
mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
printf("%d\n", ans = mp[l]);
}
return 0;
}
本文作者:realFish
本文链接:https://www.cnblogs.com/fish07/p/16208582.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步