2653: middle
分析:
二分答案+主席树。
对于中位数的经典做法,就是二分一个数,将小于的变成-1,大于等于的变成+1,那么如果sum>=0(因为+1包括等于),L=mid+1,否则R=mid-1。
那么考虑二分一个中位数(当然只二分出现过的数即可),然后向上面一样判断。
因为二分的数字只有n个,可以建立n颗只包含-1和+1的权值线段树,发现第i小的权值线段树与第i+1小的权值线段树只有一个位置不同,所以可以类似可持久化的思路,每次只去建立一条链。
查询的区间有很多个,不能挨个查询它们的和,由于查询最大值,所以可以再在线段树上维护左端最大子段和,右端最大子段和。
代码:
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<cctype> #include<set> #include<queue> #include<vector> #include<map> #define pa pair<int,int> using namespace std; typedef long long LL; inline int read() { int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; } const int N = 20005; struct Node{ int sum, Lmx, Rmx; }T[N * 20]; int ls[N * 20], rs[N * 20], Root[N], Index, n; pa A[N]; Node operator + (const Node &A,const Node &B) { Node res; res.sum = A.sum + B.sum; res.Lmx = max(A.Lmx, A.sum + B.Lmx); res.Rmx = max(B.Rmx, A.Rmx + B.sum); return res; } void build(int l,int r,int &rt) { rt = ++Index; if (l == r) { T[rt].sum = T[rt].Lmx = T[rt].Rmx = 1; return ; } int mid = (l + r) >> 1; build(l, mid, ls[rt]); build(mid + 1, r, rs[rt]); T[rt] = T[ls[rt]] + T[rs[rt]]; } void update(int l,int r,int &rt,int pre,int p) { if (!rt) rt = ++Index; if (l == r) { T[rt].sum = T[rt].Lmx = T[rt].Rmx = -1; return ; } int mid = (l + r) >> 1; if (p <= mid) { rs[rt] = rs[pre]; update(l, mid, ls[rt], ls[pre], p); } else { ls[rt] = ls[pre]; update(mid + 1, r, rs[rt], rs[pre], p); } T[rt] = T[ls[rt]] + T[rs[rt]]; } Node query(int l,int r,int rt,int L,int R) { if (L <= l && r <= R) return T[rt]; int mid = (l + r) >> 1; if (R <= mid) return query(l, mid, ls[rt], L, R); else if (L > mid) return query(mid + 1, r, rs[rt], L, R); else return query(l, mid, ls[rt], L, R) + query(mid + 1, r, rs[rt], L, R); } bool check(int x,int l1,int r1,int l2,int r2) { Node a, b, c; b.sum = 0; if (l2 > r1 + 1) b = query(1, n, Root[x], r1 + 1, l2 - 1); a = query(1, n, Root[x], l1, r1); c = query(1, n, Root[x], l2, r2); return (a.Rmx + b.sum + c.Lmx) >= 0; } int main() { n = read(); for (int i = 1; i <= n; ++i) { A[i].first = read(), A[i].second = i; } sort(A + 1, A + n + 1); build(1, n, Root[1]); for (int i = 2; i <= n; ++i) update(1, n, Root[i], Root[i - 1], A[i - 1].second); int c[5], m = read(), ans = 0, pos; while (m --) { for (int i = 0; i < 4; ++i) c[i] = (read() + ans) % n + 1; sort(c, c + 4); int l = 1, r = n; while (l <= r) { int mid = (l + r) >> 1; if (check(mid, c[0], c[1], c[2], c[3])) pos = mid, l = mid + 1; else r = mid - 1; } ans = A[pos].first; printf("%d ", ans); } return 0; }