题目描述
一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整。
给你一个长度为n的序列s。
回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。
其中a<b<c<d。
位置也从0开始标号。
我会使用一些方式强制你在线。
输入输出格式
输入格式:
第一行序列长度n。
接下来n行按顺序给出a中的数。
接下来一行Q。
然后Q行每行a,b,c,d,我们令上个询问的答案是x(如果这是第一个询问则x=0)。
令数组q={(a+x)%n,(b+x)%n,(c+x)%n,(d+x)%n}。
将q从小到大排序之后,令真正的要询问的a=q[0],b=q[1],c=q[2],d=q[3]。
输入保证满足条件。
输出格式:
Q行依次给出询问的答案。
输入输出样例
输入样例#1: 复制
5
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0
输出样例#1: 复制
271451044
271451044
969056313
说明
0:n,Q<=100
1,...,5:n<=2000
0,...,19:n<=20000,Q<=25000
题意:给N个数,Q个询问,每个询问给abcd,求[a,b]选起点和[c,d]选终点的子序列中中位数的最大值。
思路:中位数首先考虑二分答案,比它小和大的设为-1和1,取sum为[b+1,c-1]的和加上[a,b]的最大后缀加上[c,d]的最大前缀,判断sum是否>=0,但是这样每个询问复杂度都是O(nlogn)不能接受,于是用主席树预处理,就能省去设置1和-1这一步,最终单个询问复杂度为O(logn*logn)。
# include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+30;
struct node{
int num, id;
}a[maxn];
struct node2{
int lx, rx, tot;
}tree[maxn<<4];
int root[maxn]={0}, ls[maxn<<4], rs[maxn<<4], cnt=0, n;
bool cmp(node x, node y){
return x.num < y.num;
}
node2 merge(node2 x, node2 y){
node2 tmp;
tmp.tot = x.tot + y.tot;
tmp.lx = max(x.tot+y.lx, x.lx);
tmp.rx = max(y.tot+x.rx, y.rx);
return tmp;
}
void build(int &rt, int l, int r){
int tmp = rt; rt = ++cnt; tree[rt] = tree[tmp];
ls[rt] = ls[tmp]; rs[rt] = rs[tmp];
if(l == r){
tree[rt].lx = tree[rt].rx = tree[rt].tot = 1;
return;
}
int mid = l+r>>1;
build(ls[rt], l, mid);
build(rs[rt], mid+1, r);
tree[rt] = merge(tree[ls[rt]], tree[rs[rt]]);
}
void update(int &rt, int l, int r, int pos){
int tmp = rt; rt = ++cnt; tree[rt] = tree[tmp];
ls[rt] = ls[tmp]; rs[rt] = rs[tmp];
if(l == r){
tree[rt].lx = tree[rt].rx = tree[rt].tot = -1;
return;
}
int mid = l+r>>1;
if(pos <= mid) update(ls[rt], l, mid, pos);
else update(rs[rt], mid+1, r, pos);
tree[rt] = merge(tree[ls[rt]], tree[rs[rt]]);
}
node2 query(int rt, int l, int r, int L, int R){
if(L <= l && R >= r) return tree[rt];
int mid = l+r>>1;
if(R <= mid) return query(ls[rt], l, mid, L, R);
if(L > mid) return query(rs[rt], mid+1, r, L, R);
else return merge(query(ls[rt], l, mid, L, R), query(rs[rt], mid+1, r, L, R));
}
bool check(int id, int l1, int r1, int l2, int r2){
int sum = 0;
if(r1+1 < l2) sum += query(root[id], 1, n, r1+1, l2-1).tot;
sum += query(root[id], 1, n, l1, r1).rx;
sum += query(root[id], 1, n, l2, r2).lx;
return sum >= 0;
}
int main(){
int q, c[4];
scanf("%d",&n);
for(int i=1; i<=n; ++i) scanf("%d",&a[i].num), a[i].id = i;
sort(a+1, a+1+n, cmp);
build(root[1], 1, n);
for(int i=2; i<=n; ++i){
root[i] = root[i-1];
update(root[i], 1, n, a[i-1].id);
}
int ans = 0;
scanf("%d",&q);
while(q--){
for(int i=0; i<4; ++i) scanf("%d",&c[i]), c[i]=(c[i]+ans)%n;
sort(c, c+4);
for(int i=0; i<4; ++i) ++c[i];
int l = 1, r = n;
while(l <= r){
int mid = l+r>>1;
if(check(mid, c[0], c[1], c[2], c[3])) l = mid+1;
else r = mid-1;
}
printf("%d\n",a[r].num);
ans = a[r].num;
}
return 0;
}