中位数相关
中位数的查询方式:
1.对顶堆动态维护
2.主席树--区间K小
3.二分答案 >=mid 染成1,<mid染成-1,求和>=0(这个求和要看具体题目中对中位数的定义)
主席树---G. middle
暴力1 TLE 5

#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 2e4 + 7; int n, q[6], ans, a[maxn], b[maxn], cnt, Q, root[maxn]; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } struct seg { struct node { int sum; }t[maxn<<5]; int lc[maxn<<5], rc[maxn<<5], tot; void build(int &x, int l, int r) { x = ++tot; if(l == r) return; int mid = (l + r) >> 1; build(lc[x], l, mid); build(rc[x], mid+1, r); } void update(int &x, int pre, int l, int r, int pos) { x = ++tot; lc[x] = lc[pre], rc[x] = rc[pre]; t[x].sum = t[pre].sum + 1; if(l == r) return; int mid = (l + r) >> 1; if(pos <= mid) update(lc[x], lc[pre], l, mid, pos); else update(rc[x], rc[pre], mid+1, r, pos); } int query(int x, int pre, int l, int r, int L, int R, int k) { if(l == r) return b[l]; int mid = (l + r) >> 1; int lsize = t[lc[x]].sum - t[lc[pre]].sum; if(lsize >= k) return query(lc[x], lc[pre], l, mid, L, R, k); else return query(rc[x], rc[pre], mid+1, r, L, R, k-lsize); } }t; int main() { n = read(); for(int i=1; i<=n; i++) { a[i] = read(); b[i] = a[i]; } sort(b+1, b+1+n); cnt = unique(b+1, b+1+n)-b-1; t.build(root[0], 1, cnt); for(int i=1; i<=n; i++) { int x = lower_bound(b+1, b+1+cnt, a[i])-b; t.update(root[i], root[i-1], 1, cnt, x); } Q = read(); while(Q--) { q[0] = read(); q[1] = read(); q[2] = read(); q[3] = read(); q[0] = (q[0] + ans) % n + 1, q[1] = (q[1] + ans) % n + 1; q[2] = (q[2] + ans) % n + 1; q[3] = (q[3] + ans) % n + 1; sort(q, q+4); //l = (q[0] + q[3]) / 2; r = () ans = 0; //printf("%d %d %d %d\n", q[0], q[1], q[2], q[3]); for(int i=q[0]; i<=q[1]; i++) { for(int j=q[2]; j<=q[3]; j++) { int k = (j - i + 1) / 2 + 1; //printf("i = %d j = %d k = %d\n", i, j, k); int w = t.query(root[j], root[i-1], 1, n, i, j, k); //printf("w = %d\n", w); ans = max(ans, w); } } printf("%d\n", ans); } return 0; }
暴力2 TLE 15,不知道为什么别人TLE 20,难道是常数?

#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 2007; int ans, A[maxn], n, mid[maxn][maxn], q[6], Q; priority_queue<int> a; priority_queue<int, vector<int>, greater<int> > b; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } void Push(int x) { if(a.empty() || x<=a.top()) a.push(x); else b.push(x); while(a.size() < b.size() + 1) { a.push(b.top()); b.pop(); } while(a.size() > b.size()) { b.push(a.top()); a.pop(); } } int main() { n = read(); for(int i=1; i<=n; i++) A[i] = read(); for(int i=1; i<=n; i++) { while(!a.empty()) a.pop(); while(!b.empty()) b.pop(); for(int j=i; j<=n; j++) { Push(A[j]); mid[i][j] = b.top(); } } Q = read(); while(Q--) { int a = read(), b = read(), c = read(), d = read(); a = (a+ans)%n+1, b = (b+ans)%n+1, c = (c+ans)%n+1, d = (d+ans)%n+1; q[0] = a, q[1] = b, q[2] = c; q[3] = d; sort(q, q+4); a = q[0], b = q[1], c = q[2], d = q[3]; ans = 0; for(int i=a; i<=b; i++) { for(int j=c; j<=d; j++) { ans = max(ans, mid[i][j]); } } printf("%d\n", ans); } return 0; }
正解 找到有可能成为中位数的最大的mid,为了让它尽量满足条件,把[a, b]的最大后缀,(b, c)的区间和,[c, d]的最大前缀加起来判断。

#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 5e5 + 3; int n, g[maxn], v[maxn], m, ans, root[maxn], q[6]; int a, b, c, d; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } inline bool cmp(int x, int y) {return g[x] < g[y];} struct seg { struct node { int sum, pre, suf; }t[maxn<<5]; int lc[maxn<<5], rc[maxn<<5], tot; inline void pushup(int x) { t[x].sum = t[lc[x]].sum + t[rc[x]].sum; t[x].pre = max(t[lc[x]].pre, t[lc[x]].sum + t[rc[x]].pre); t[x].suf = max(t[rc[x]].suf, t[rc[x]].sum + t[lc[x]].suf); } void build(int &x, int l, int r) { x = ++tot; if(l == r) { t[x].sum = t[x].pre = t[x].suf = -1; return; } int mid = (l + r) >> 1; build(lc[x], l, mid); build(rc[x], mid+1, r); pushup(x); } /*void update(int &x, int pre, int l, int r, int pos) { if(!x) x = ++tot; if(l == r) { t[x].sum = t[x].pre = t[x].suf = 1; return; } int mid = (l + r) >> 1; if(pos <= mid) rc[x] = rc[pre], update(lc[x], lc[pre], l, mid, pos); else lc[x] = lc[pre], update(rc[x], rc[pre], mid+1, r, pos); pushup(x); }*/ void update(int &x, int pre, int l, int r, int pos) { x = ++tot; lc[x] = lc[pre], rc[x] = rc[pre]; if(l == r) { t[x].sum = t[x].pre = t[x].suf = 1; return; } int mid = (l + r) >> 1; if(pos <= mid) update(lc[x], lc[pre], l, mid, pos); else update(rc[x], rc[pre], mid+1, r, pos); pushup(x); } node query(int x, int l, int r, int L, int R) { if(L <= l && r <= R) return t[x]; int mid = (l + r) >> 1; if(L > mid) return query(rc[x], mid+1, r, L, R); else if(R <= mid) return query(lc[x], l, mid, L, R); else { node ans, ls = query(lc[x], l, mid, L, R), rs = query(rc[x], mid+1, r, L, R); ans.sum = ls.sum + rs.sum; ans.pre = max(ls.pre, ls.sum + rs.pre); ans.suf = max(rs.suf, rs.sum + ls.suf); return ans; } } }t; inline bool check(int mid) { int res = 0; seg::node ans; ans = t.query(root[mid], 1, n, a, b); res += ans.suf; if(b+1 <= c-1) ans = t.query(root[mid], 1, n, b+1, c-1), res += ans.sum; ans = t.query(root[mid], 1, n, c, d); res += ans.pre; return res >= 0; } int main() { n = read(); for(int i=1; i<=n; i++) { g[i] = read(); v[i] = i; } sort(v+1, v+1+n, cmp); t.build(root[n+1], 1, n); for(int i=n; i; i--) t.update(root[i], root[i+1], 1, n, v[i]); m = read(); while(m--) { a = read(), b = read(), c = read(), d = read(); a = (a + ans) % n + 1, b = (b + ans) % n + 1; c = (c + ans) % n + 1, d = (d + ans) % n + 1; q[1] = a, q[2] = b, q[3] = c, q[4] = d; sort(q+1, q+1+4); a = q[1], b = q[2], c = q[3], d = q[4]; int l = 1, r = n; while(l < r) { int mid = (l + r + 1) >> 1; if(check(mid)) l = mid; else r = mid - 1; } ans = g[v[l]]; printf("%d\n", ans); } return 0; }
来自学长的推荐---D. 题目难度提升
https://blog.csdn.net/qq_36797743/article/details/83303549
1.没有重复数字时,先填入最小的,因为选择的第一个数会作为中位数,那么第二个就一定要比它大才能使中位数单调不降,所以中位数变得更大了,如果第三个数比第一个还小,那中位数回到第一个它不合法了所以中位数又变大了……类推下去发现比第一个数还小的数一定放不进去,所以第一个数一定要放最小的。
第一个数放完了,后面的数就都可以贪心解决了——每次都在没填的数中选尽可能大的一个,满足把它填进去之后,剩下的数里最小的数不比中位数小(没有重复数字时,新加入的数比中位数大就使中位数变大,反之就使中位数变小)。具体的实现方法就是用对顶堆来维护一下中位数并且用multiset维护一下未填数集(开multiset是为了下一种可以重复的情况)。
https://blog.csdn.net/chenyume/article/details/89045255 这里有关于对顶堆的一些解释,不过在这篇题解中用法和介绍里有些不同,a是一个从大到小排列的堆,里面放的是(奇数)1~n/2+1/(偶数)1~n/2的值,b就是剩下的从小到大排列,如果它维护的区间有奇数个元素,中位数就是a.top(),如果是偶数中位数就是(a.top()+b.top())/2,为了让b小的当top可以把b存相反数,中位数就变成(a.top()-b.top())/2。
现在最小的数已经填上了,考虑剩下的:如果已填数集合中有偶数个数,当前中位数是a.top()和b.top()的平均数,填入一个比中位数更大的数(不可避免)后中位数向大偏移,这时有两种情况,要么变成b.top(),要么变成NewOne(b.top()>NewOne)……(详见代码里的注释)。如果已填数集合中有奇数个数,新加入一个数后的中位数也是有两种情况,要么变成(a.top()+b.top())/2,要么变成(a.top()+NewOne)/2(NewOne<b.top())显然它的值比上一种情况小,如果最小的未填数比最大可能的新中位数还大,那就随便填吧,否则就需要x>=(a.top()+NewOne)/2,移项可得NewOne<=2*x-a.top(),其中NewOne是小于等于里最大的,所以用upper_bound-1,它的最坏情况是x。
2.有重复数字时,先考虑重复数字。在可重情况下有一个非常好用的规律——如果中间有两个相等的数,那就让他们一直作为中位数一小一大地往后填,每次选的恰好就是当前能填入的合法数中最大的一个,这个结论还可以推广一下——又有两种情况:如果重复的数在中点之后(当然是排过序的),因为最后的中位数一定会小于它,所以它永远不会成为中位数,推广失败,把它看成不相等的就好了;如果在中点之前,结论就又可以应用了,让重复的哪些数成为前半段的中位数就好(这里的前半段不是指区间,而是指时间,这个一半也不是准确的一半,只能说是一段,因为它的值偏小,填完这一段后中位数一定会向上偏移),预处理完相等的这些情况之后,就可以和不相等的情况一样考虑了。

#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5 + 2; const ll mod = 1e9 + 7; const int INF = 2147483647; const int lim = 1e4 + 1; int A[maxn], n; bool ok[maxn]; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch > '9' || ch < '0') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } priority_queue<int> a, b; void Push(int x) { if(a.empty() || x<=a.top()) a.push(x);//a : da->xiao else b.push(-x);//b: xiao->da if(a.size()<b.size()) { int x = b.top(); b.pop(); a.push(-x);//a b 的相反由定义决定 } if(a.size()>b.size()+1) { int x = a.top(); a.pop(); b.push(-x); } } multiset<int> s; multiset<int>::iterator it; int main() { freopen("d.in", "r", stdin); freopen("d.out", "w", stdout); n = read(); for(int i=1; i<=n; i++) { scanf("%d", &A[i]); } sort(A+1, A+1+n); int mid = (n + 1) >> 1; if(A[mid] == A[mid+1]) { while(A[mid] == A[mid+1]) mid++; printf("%d ", A[mid]); int p = n, q = mid - 1; while(p>mid || q>0) { if(q>0) printf("%d ", A[q--]); if(p>mid) printf("%d ", A[p--]); } exit(0); } while(mid>1 && A[mid-1]!=A[mid]) mid--; ok[mid] = 1; printf("%d ", A[mid]); int p=n, q=mid-1; while(p>mid && q>0) { ok[q] = 1; printf("%d ", A[q--]); ok[p] = 1; printf("%d ", A[p--]); } for(int i=1; i<=n; i++) { if(ok[i]) Push(A[i]);//已经填入的数,用两个堆来维护中位数 else s.insert(A[i]);//没填的数,在每一位填之前,寻找一个我们能填的最大的数, //且满足填了以后,最小的数还是不比中位数小 } while(!s.empty()) { int x = *s.begin();//未填入的数中最小的那个 int ans; //事实上未填数集合中的所有数都比当前中位数大,所以以下if如果不被满足就会不合法 if(a.size() == b.size())//b.top()是填入一个中位数比当前中位数大的数后,中位数会变成的最大可能 { if(x>=(-b.top())) ans = *(--s.end());//如果它的影响不足以使最小的未填数填不进去,直接从未填的数里选最大的 else ans = x; } else { if(!b.empty()&&(x*2>=a.top()-b.top())) ans = *(--s.end()); else { ans = *(--s.upper_bound(x*2-a.top())); } } printf("%d ", ans); Push(ans); s.erase(s.find(ans)); } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下