题意

一个长度为\(n\)的序列\(a\),设其排过序之后为\(b\),其中位数定义为\(b[n/2]\),其中\(a,b\)从\(0\)开始标号,除法取下整。给你一个长度为\(n\)的序列\(s\)。回答\(Q\)个这样的询问:\(s\)的左端点在\([a,b]\)之间,右端点在\([c,d]\)之间的子序列中,最大的中位数。其中\(a<b<c<d\)。位置也从\(0\)开始标号。强制在线。

题解

比较套路地,我们考虑二分这个中位数(设为当前\(mid\)),如果它偏左就往右移,否则往左移

为了方便,若\(x<mid\),它的贡献是\(-1\),否则是\(1\),这样我们只要看总贡献的正负就行

我们就求出\([b + 1, c - 1]\)的贡献,加上\([a, b]\)的最大后缀和\([c, d]\)的最大前缀

我们考虑怎么求一个区间\([l, r]\)对\(c\)的贡献的前缀max,后缀max,区间和。如果对下标开主席树,区间和可以,但前两个操作不太行。

考虑两维互换。先考虑\([1, n]\)对\(1\)(注意这里已经离散化过了)的贡献,然后再移动到\([1, n]\)对\(2\)的贡献。我们发现总共只有\(n\)次\(1\)被改成\(-1\)的操作,就可以主席树了

询问的时候只要在一棵主席树上查询就行了,因为我们维护的并不是前缀信息。

#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std; const int N = 3e4 + 10;
const int M = N * 40;
int n, q, a[N], num[N], b[N], T[N], ls[M], rs[M], id;
vector<int> pos[N];
struct Node {
int s, lm, rm;
void init(int x) { s = lm = rm = x; }
} t[M];
void merge(Node &ans, const Node &l, const Node &r) {
ans.s = l.s + r.s; ans.lm = max(l.lm, l.s + r.lm); ans.rm = max(r.rm, r.s + l.rm);
}
void build(int &u, int l, int r) {
u = ++ id;
if(l == r) { t[u].init(1); return ; }
int mid = (l + r) >> 1;
build(ls[u], l, mid);
build(rs[u], mid + 1, r);
merge(t[u], t[ls[u]], t[rs[u]]);
}
void ins(int &u, int p, int l, int r, int x) {
u = ++ id; t[u] = t[p]; ls[u] = ls[p]; rs[u] = rs[p];
if(l == r) { t[u].init(-1); return ; }
int mid = (l + r) >> 1;
if(x <= mid) ins(ls[u], ls[p], l, mid, x);
else ins(rs[u], rs[p], mid + 1, r, x);
merge(t[u], t[ls[u]], t[rs[u]]);
}
Node qry(int u, int l, int r, int ql, int qr) {
if(l == ql && r == qr) return t[u];
int mid = (l + r) >> 1;
if(qr <= mid) return qry(ls[u], l, mid, ql, qr);
if(ql > mid) return qry(rs[u], mid + 1, r, ql, qr);
Node ans;
merge(ans, qry(ls[u], l, mid, ql, mid), qry(rs[u], mid + 1, r, mid + 1, qr));
return ans;
}
int qry_sum(int u, int l, int r, int ql, int qr) {
if(l == ql && r == qr) return t[u].s;
int mid = (l + r) >> 1;
if(qr <= mid) return qry_sum(ls[u], l, mid, ql, qr);
if(ql > mid) return qry_sum(rs[u], mid + 1, r, ql, qr);
return qry_sum(ls[u], l, mid, ql, mid) + qry_sum(rs[u], mid + 1, r, mid + 1, qr);
}
int calc(int a, int b, int c, int d, int mid) {
int ans = qry(T[mid], 1, n, a, b).rm + qry(T[mid], 1, n, c, d).lm;
if(b + 1 < c) ans += qry_sum(T[mid], 1, n, b + 1, c - 1);
return ans;
}
int solve(int a, int b, int c, int d) {
int l = 1, r = n, mid;
while(l <= r) {
mid = (l + r) >> 1;
if(calc(a, b, c, d, mid) >= 0) l = mid + 1;
else r = mid - 1;
}
return num[l - 1];
}
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i ++) scanf("%d", a + i), num[i] = a[i];
sort(num + 1, num + n + 1);
for(int i = 1; i <= n; i ++) b[i] = lower_bound(num + 1, num + n + 1, a[i]) - num, pos[b[i]].push_back(i);
build(T[1], 1, n);
for(int i = 2; i <= n; i ++) {
T[i] = T[i - 1];
for(int j = 0; j < pos[i - 1].size(); j ++)
ins(T[i], T[i], 1, n, pos[i - 1][j]);
}
scanf("%d", &q);
int arr[4], la_ans = 0;
for(int i = 1; i <= q; i ++) {
scanf("%d%d%d%d", arr, arr + 1, arr + 2, arr + 3);
for(int j = 0; j < 4; j ++) arr[j] = (arr[j] + la_ans) % n;
sort(arr, arr + 4);
printf("%d\n", la_ans = solve(arr[0] + 1, arr[1] + 1, arr[2] + 1, arr[3] + 1));
}
return 0;
}

最新文章

  1. 手机端html5触屏事件(touch事件)
  2. ZigZag Conversion leetcode java
  3. 经历alidns在国外的严重延时
  4. javascript性能优化总结一(转载人家)
  5. CNN 手写数字识别
  6. NYU Hand Pose Dataset
  7. 转!!Java中关于Null的9个解释(Java Null详解)
  8. UIView的创建与内存管理
  9. 关于“undefined reference to”错误
  10. 【UVA 10307 Killing Aliens in Borg Maze】最小生成树, kruscal, bfs
  11. LevelDB架构
  12. 十六、Hadoop学习笔记————Zookeeper实战
  13. ubuntu安装qq教程
  14. 代码方式设置WordPress内所有URL链接都在新标签页打开
  15. sweetalert提示框
  16. Nuxt框架,ssr服务器渲染解决单页面应用的 SEO 问题
  17. TCP相关面试题(转)
  18. ES6 入门Promise
  19. /dev/i2c-*不见了
  20. Java课程作业之动手动脑(四)

热门文章

  1. Django 在Python3.5 下报 没有模块MySQLdb
  2. 3-MySQL DBA笔记-开发基础
  3. 如何给Swagger加注释
  4. SQLSERVER 20018 R2 T-SQL 创建linkServer
  5. [转载]SSD原理与实现
  6. Python爬取爱奇艺资源
  7. Git忽略已追踪文件或文件夹
  8. wepy2.0中使用vant-weapp组件
  9. 【atcoder】Enclosed Points [abc136F]
  10. MYSQL 遇见各种有意思题库