P2839 [国家集训队]middle
有一个长度为nnn的序列,有mmm次询问,每次询问a,b,c,da, b, c, da,b,c,d,为l∈[a,b],r∈[c,d]l \in [a, b], r \in [c, d]l∈[a,b],r∈[c,d],[l,r][l, r][l,r]区间的中位数最大是多少,强制在线,1≤n≤20000,1≤m≤250001 \leq n \leq 20000, 1 \leq m \leq 250001≤n≤20000,1≤m≤25000。
由于有a≤b≤c≤da \leq b \leq c \leq da≤b≤c≤d,分三个区间讨论[a,b],[b+1,c−1],[c,d][a, b], [b + 1, c - 1], [c, d][a,b],[b+1,c−1],[c,d],其中[b+1,c−1][b + 1, c - 1][b+1,c−1]这个区间是必选的,[a,b],[c,d][a, b], [c, d][a,b],[c,d]这俩个区间可选可不选,
我们考虑二分答案,对于mid≤aimid \leq a_imid≤ai的我们设置为111,否则我们设置为−1-1−1,对于选定的区间,如果中位数是等于midmidmid的,则一定有区间和>=0>= 0>=0,
由于我们要使区间中位数尽可能地大,所以我们定然是在[a,b][a, b][a,b]上选一个点ppp在1,−11, -11,−1序列上使得[p,b][p, b][p,b]的和最大,在[c,d][c, d][c,d]上选定一个点qqq也是如此。
如此做的话,单次询问复杂度将会是O(nlognlogn)O(n \log n \log n )O(nlognlogn)的,有没有办法高效来完成呢,整体二分似乎是一个比较好的方法,但是这题要求在线求解,,,
整理一下上面我们所需要的值,[b+1,c−1][b + 1, c - 1][b+1,c−1]的区间和,[a,b][a, b][a,b]区间的后缀最大值,[c,d][c, d][c,d]区间的前缀最大值,
对于任意一个区间的后缀最大值,前缀最大值都可用线段树维护,同样的区间和也可以用线段树维护,
目前我们所需的就是对于任何一个midmidmid,我们要精确地知道对于某段区间地前缀最大,前缀最小,区间和,
这里就可以考虑用主席树,对每个二分地midmidmid都建立一颗主席树,对于每次二分地midmidmid是否合法,我们只要到对应地主席树上查找即可。
#include <bits/stdc++.h>using namespace std;const int N = 2e4 + 10;struct Seg {int l, r, sum;
}t[N << 6];int root[N], ls[N << 6], rs[N << 6], num;int a[N], b[N], n, m;Seg operator + (Seg a, Seg b) {Seg ans;ans.sum = a.sum + b.sum;ans.l = max(a.l, a.sum + b.l);ans.r = max(b.r, b.sum + a.r);return ans;
}void push_up(int rt) {t[rt] = t[ls[rt]] + t[rs[rt]];
}void build(int &rt, int l, int r) {rt = ++num;if (l == r) {t[rt] = {-1, -1, -1};return ;}int mid = l + r >> 1;build(ls[rt], l, mid);build(rs[rt], mid + 1, r);push_up(rt);
}void update(int &rt, int pre, int l, int r, int x) {rt = ++num, ls[rt] = ls[pre], rs[rt] = rs[pre];if (l == r) {t[rt] = {1, 1, 1};return ;}int mid = l + r >> 1;if (x <= mid) {update(ls[rt], ls[pre], l, mid, x);}else {update(rs[rt], rs[pre], mid + 1, r, x);}push_up(rt);
}Seg query(int rt, int l, int r, int L, int R) {if (l >= L && r <= R) {return t[rt];}int mid = l + r >> 1;if (L <= mid && R > mid) {return query(ls[rt], l, mid, L, R) + query(rs[rt], mid + 1, r, L, R);}else if (L <= mid) {return query(ls[rt], l, mid, L, R);}else {return query(rs[rt], mid + 1, r, L, R);}
}int aa, bb, cc, dd;bool judge(int x) {int res = 0;res += query(root[x], 1, n, aa, bb).r;if (bb + 1 <= cc - 1) {res += query(root[x], 1, n, bb + 1, cc - 1).sum;}res += query(root[x], 1, n, cc, dd).l;return res >= 0;
}int main() {// freopen("in.txt", "r", stdin);// freopen("out.txt", "w", stdout);scanf("%d", &n);for (int i = 1; i <= n; i++) {scanf("%d", &a[i]);b[i] = i;}scanf("%d", &m);build(root[n + 1], 1, n);sort(b + 1, b + 1 + n, [&] (int x, int y) {return a[x] < a[y];});for (int i = n; i >= 1; i--) {update(root[i], root[i + 1], 1, n, b[i]);}int ans = 0;for (int i = 1; i <= m; i++) {scanf("%d %d %d %d", &aa, &bb, &cc, &dd);aa = (aa + ans) % n + 1, bb = (bb + ans) % n + 1;cc = (cc + ans) % n + 1, dd = (dd + ans) % n + 1;int tmp[5] = {0, aa, bb, cc, dd};sort(tmp + 1, tmp + 4 + 1);aa = tmp[1], bb = tmp[2], cc = tmp[3], dd = tmp[4];int l = 1, r = n;while (l < r) {int mid = l + r + 1 >> 1;if (judge(mid)) {l = mid;}else {r = mid - 1;}}ans = a[b[l]];printf("%d\n", ans);}return 0;
}