题目大意:给你一个长度为$n$的序列$s$。$Q$个询问,问在$s$中的左端点在$[a,b]$之间,右端点在$[c,d]$之间的子段中,最大的中位数。 强制在线。
题解:区间中位数?二分答案,如果询问区间是给定的,对于每个询问,二分答案是多少,然后只要求出这个区间中有多少个数比二分的数大就行了,这就可以对每一个值建一棵主席树,把比它小的赋成$-1$,大于等于的赋成$1$,只需要区间和,就可以在$O(\log_2 n)$的时间判断一个解了。
但区间不给定。怎么办?注意到,$[b+1,c-1]$的值是必选的。所以一下这个区间的和是一定会产生贡献的。
而对于$[a,b],[c,d]$,因为要让中位数尽可能的大。所以,要让这里面的数比二分的答案大的数尽可能多。
也就是说,需要找一个$[a,b]$的最大后缀和,$[c,d]$的最大前缀和。这三个值的和就是给的询问中所有字段中对于二分的答案的最大的值了(也就是比二分答案大的数的个数减去比二分答案小的数的个数),也就是最优解。
问题是,我们二分的中位数不一定在询问的范围当中,会不会最后的答案不在这个区间内呢? 其实是不会的,如果区间外有个数满足要求,那么区间内一定会有个数大于等于它,显然区间内的那个数最优,而且也是满足中位数的要求的
卡点:1.求区间最大前缀和以及区间最大后缀和的线段树(主席树),的判断返回值条件和分治方法和普通的不同,而我按普通的在写
2.我二分的答案是这个数是所有数中第几大的,然后就把$lastans$赋成了这个(应该赋成这个数是多少)
3.洛谷有锅,一模一样的代码在洛谷上$30$,在$darkbzoj$和$bzoj$上$AC$(虽然后来也在洛谷上过了。。。。)
C++ Code:
#include#include #define maxn 200100#define N 3001500using namespace std;int root[maxn], lc[N], rc[N];int idx;bool flag;struct node { int r, sum, l; //r后缀,l前缀 inline bool operator == (node rhs) {return (r == rhs.r && sum == rhs.sum && l == rhs.l);}} V[N], err;int n, Q, s[maxn], rnk[maxn];int p[5], lastans = 0, ans;inline bool cmp(int a, int b) {return s[a] < s[b];}inline int max(int a, int b) {return a > b ? a : b;}void update(int rt) { V[rt].r = max(V[rc[rt]].r, V[rc[rt]].sum + V[lc[rt]].r); V[rt].l = max(V[lc[rt]].l, V[lc[rt]].sum + V[rc[rt]].l); V[rt].sum = V[lc[rt]].sum + V[rc[rt]].sum;}void build(int &rt, int l, int r) { rt = ++idx; if (l == r) { V[rt].r = V[rt].sum = V[rt].l = 1; return ; } int mid = l + r >> 1; build(lc[rt], l, mid); build(rc[rt], mid + 1, r); update(rt);}void add(int &rt, int l, int r, int p) { lc[++idx] = lc[rt], rc[idx] = rc[rt], rt = idx; if (l == r) { V[rt].r = V[rt].sum = V[rt].l = -1; return ; } int mid = l + r >> 1; if (p <= mid) add(lc[rt], l, mid, p); else add(rc[rt], mid + 1, r, p); update(rt);}int a, b, c, d;node ask(int rt, int l, int r, int L, int R) { if (!rt || l > r || L > R) return err; if (L == l && R == r) return V[rt]; int mid = l + r >> 1; if (R <= mid) return ask(lc[rt], l, mid, L, R); if (L > mid) return ask(rc[rt], mid + 1, r, L, R); node ans = ask(lc[rt], l, mid, L, mid), tmp = ask(rc[rt], mid + 1, r, mid + 1, R); ans.l = max(ans.l, ans.sum + tmp.l); ans.r = max(tmp.r, tmp.sum + ans.r); ans.sum = ans.sum + tmp.sum; return ans;}bool check(int mid) { int tmp = ask(root[mid], 1, n, a, b).r + ask(root[mid], 1, n, b + 1, c - 1).sum + ask(root[mid], 1, n, c, d).l; return tmp >= 0;}int main() { scanf("%d", &n); build(root[1], 1, n); for (int i = 1; i <= n; i++) scanf("%d", &s[i]), rnk[i] = i; sort(rnk + 1, rnk + n + 1, cmp); for (int i = 2; i <= n; i++) { root[i] = root[i - 1]; add(root[i], 1, n, rnk[i - 1]); } scanf("%d", &Q); while (Q --> 0) { scanf("%d%d%d%d", &a, &b, &c, &d); p[0] = (a + lastans) % n + 1, p[1] = (b + lastans) % n + 1, p[2] = (c + lastans) % n + 1, p[3] = (d + lastans) % n + 1; sort(p, p + 4); a = p[0], b = p[1], c = p[2], d = p[3]; int L = 1, R = n; while (L <= R) { int mid = L + R + 1 >> 1; if (check(mid)) { L = mid + 1; ans = mid; } else R = mid - 1; } printf("%d\n", s[rnk[ans]]); lastans = s[rnk[ans]]; } return 0;}