P2839 [国家集训队]middle
一个长度为 \(n\) 的序列 \(a\),设其排过序之后为 \(b\),其中位数定义为 \(b_{n/2}\),其中 \(a,b\) 从 \(0\) 开始标号,除法取下整。
给你一个长度为 \(n\) 的序列 \(s\)。
回答 \(Q\) 个这样的询问:\(s\) 的左端点在 \([a,b]\) 之间,右端点在 \([c,d]\) 之间的子区间中,最大的中位数。
其中 \(a<b<c<d\)。
位置也从 \(0\) 开始标号。
我会使用一些方式强制你在线。
比较清新的难题,这次我终于没有抄代码自己写出来了……
这里要求最大中位数,可以考虑二分答案。将大于等于mid的数设为1,小于mid的数设为0。
如果一个区间的和大于等于0,那么显然答案大于等于mid。
所以询问其实就是要找左端点在[a,b],右端点在[c,d]的最大的区间和,[b+1, c-1]的和是固定的,问题转化为对[a,b], [c,d]区间分别求右侧的最大子段和和左侧的最大子段和。
这个可以用线段树维护。离散化之后,我们的mid只有n个不同的取值。
对于mid 和 mid + 1,其实只有等于mid的那个数的位置发生了变化。然后惊讶发现每次只改变一个位置的值不是主席树吗。
主席树比较容易错的地方就是建树的时候应该按照什么样的顺序,这里要按照数的大小顺序。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fi first
#define se second
using namespace std;
const int N = 1e5 + 100;
typedef long long ll;
typedef pair<ll, ll> pll;
namespace Segment{
int rt[N<<5], ls[N << 5], rs[N << 5];
ll lmx[N << 5], rmx[N << 5], sum[N << 5], mx[N << 5];
int tot;
void push_up(int x) {
sum[x] = sum[ls[x]] + sum[rs[x]];
lmx[x] = max(lmx[ls[x]], sum[ls[x]] + lmx[rs[x]]);
rmx[x] = max(rmx[rs[x]], sum[rs[x]] + rmx[ls[x]]);
mx[x] = max(lmx[x], rmx[x]);
}
void build(int &x,int l, int r) {
if (!x) x = ++tot;
if (l == r) {
lmx[x] = rmx[x] = mx[x] = 1;
sum[x] = 1;
return ;
}
int mid = (l + r) >> 1;
build(ls[x], l, mid);
build(rs[x], mid + 1, r);
push_up(x);
}
void insert(int y,int &x, int l, int r, int pos) {
if (!x) x = ++tot;
if (l == r) {
lmx[x] = rmx[x] = mx[x] = 0;
sum[x] = -1;
return ;
}
int mid = (l + r) >> 1;
if (pos <= mid) insert(ls[y],ls[x], l, mid, pos), rs[x] = rs[y];
else insert(rs[y], rs[x], mid + 1, r, pos), ls[x] = ls[y];
push_up(x);
}
pll query_lmx(int x, int l, int r, int L, int R) {
if (l > R || r < L) return make_pair(0,0);
if (L <= l && r <= R) {
return make_pair(lmx[x], sum[x]);
}
int mid = (l + r) >> 1;
pll i = query_lmx(ls[x], l, mid, L, R);
pll j = query_lmx(rs[x], mid + 1, r, L, R);
return make_pair(max(i.first, i.second + j.first), i.second + j.second);
}
pll query_rmx(int x, int l, int r, int L, int R) {
if (l > R || r < L) return make_pair(0, 0);
if (L <= l && r <= R) {
return make_pair(rmx[x], sum[x]);
}
int mid = (l + r) >> 1;
pll i = query_rmx(ls[x], l, mid, L, R);
pll j = query_rmx(rs[x], mid + 1, r, L, R);
return make_pair(max(j.first, i.first + j.second), i.second + j.second);
}
long long query_sum(int x, int l, int r, int L ,int R) {
if (l > R || r < L ) return 0;
if (L <= l && r <= R) {
return sum[x];
}
int mid = (l + r) >> 1;
return query_sum(ls[x], l, mid, L, R) + query_sum(rs[x], mid + 1, r , L, R);
}
}
using namespace Segment;
int a[N], b[N], n, m, lastans;
int p[N];
int tmp[5];
bool cmp(int x, int y) {
return a[x] < a[y];
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]), b[i] = a[i], p[i] = i;
sort(b + 1, b + n + 1);
sort(p + 1, p + n + 1, cmp);
for (int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + n + 1, a[i]) - b;
//for (int i = 1; i <= n; i++) cout << a[i] << " " ; cout << endl;
Segment::build(rt[1], 1, n);
for (int i = 2; i <= n; i++) {
insert(rt[i-1], rt[i], 1, n, p[i-1]);
}
scanf("%d", &m);
for (int i = 1; i <= m; i++) {
int A, B, C, D;
scanf("%d%d%d%d", &A, &B, &C, &D);
A = (A + lastans) % n;tmp[1] = A+1;
B = (B + lastans) % n;tmp[2] = B+1;
C = (C + lastans) % n;tmp[3] = C+1;
D = (D + lastans) % n;tmp[4] = D+1;
sort(tmp + 1, tmp + 5);
A = tmp[1], B = tmp[2], C = tmp[3], D = tmp[4];
int l = 1, r = n;
while (l < r) {
int mid = (l + r + 1) >> 1;
//cout << "mid !! " << mid << endl;
ll s = query_lmx(rt[mid], 1, n, C+1, D).first + query_rmx(rt[mid], 1, n, A, B-1).first + query_sum(rt[mid], 1, n, B, C);
//cout << "s " << s << endl;
if (s >= 0) l = mid;
else r = mid - 1;
}
printf("%d\n", b[l]);
lastans = b[l];
}
}

浙公网安备 33010602011771号