P2839 [国家集训队]middle
LuoP2839 [国家集训队]middle
主席树好题,点个赞!
这道题让我知道了,主席树不只是维护权值线段树。也是可以去像普通线段树一样操作的.
首先,关于求区间中位数的一个小\(trick\)(反正我之前没听说过)
二分答案
将大于等于\(mid\)的值看做\(1\),小于\(mid\)的看做\(-1\)
那么如果区间和大于等于\(0\),则说明区间中位数大于等于\(mid\)(因为此题定义偶数个元素时,中位数为中间靠前的一个,如果是靠后的就要严格大于了)
之后,发现这个左端点和右端点不固定有点恶心.但是\([b + 1,c - 1]\)是一定要选的,所以\([b + 1,c - 1]\)这个区间和的贡献是要算的
所以我们先算这一区间的值。然后区间\([a,b]\),\([c,d]\).我们最优选,就是最大前缀后缀和啊
所以要维护区间和,最大左起子段和,最大右起子段和
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cctype>
#define LL long long
const int N = 2e4 + 3;
struct node{
int sum;
int lsum,rsum;
int lc,rc;
}a[N << 6];
struct inf{
LL val;
int pos;
}v[N];
int rt[N];
int n,q,t;
inline LL read(){
LL v = 0,c = 1;char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') c = -1;
ch = getchar();
}
while(isdigit(ch)){
v = v * 10 + ch - 48;
ch = getchar();
}
return v * c;
}
inline void pushup(int u){
a[u].sum = a[a[u].lc].sum + a[a[u].rc].sum;
a[u].lsum = std::max(a[a[u].lc].lsum,a[a[u].lc].sum + a[a[u].rc].lsum);
a[u].rsum = std::max(a[a[u].rc].rsum,a[a[u].rc].sum + a[a[u].lc].rsum);
}
inline void build(int &u,int l,int r){
u = ++t;
if(l == r){
a[u].sum = a[u].lsum = a[u].rsum = 1;
return ;
}
int mid = (l + r) >> 1;
build(a[u].lc,l,mid);
build(a[u].rc,mid + 1,r);
pushup(u);
}
inline void ins(int &u,int l,int r,int x){
a[++t] = a[u];
u = t;
if(l == r){
a[u].sum = -1;
a[u].lsum = -1;
a[u].rsum = -1;
return ;
}
int mid = (l + r) >> 1;
if(x <= mid) ins(a[u].lc,l,mid,x);
else ins(a[u].rc,mid + 1,r,x);
pushup(u);
}
inline int query_sum(int u,int l,int r,int ll,int rr){
if(rr < ll) return 0;
if(l == ll && r == rr) return a[u].sum;
int mid = (l + r) >> 1;
if(rr <= mid) return query_sum(a[u].lc,l,mid,ll,rr);
else if(ll > mid) return query_sum(a[u].rc,mid + 1,r,ll,rr);
else return query_sum(a[u].lc,l,mid,ll,mid) + query_sum(a[u].rc,mid + 1,r,mid + 1,rr);
}
inline int query_lsum(int u,int l,int r,int ll,int rr){
if(rr < ll) return 0;
if(l == ll && r == rr) return a[u].lsum;
int mid = (l + r) >> 1;
if(rr <= mid) return query_lsum(a[u].lc,l,mid,ll,rr);
else if(ll > mid) return query_lsum(a[u].rc,mid + 1,r,ll,rr);
else return std::max(query_lsum(a[u].lc,l,mid,ll,mid),query_sum(a[u].lc,l,mid,ll,mid)
+ query_lsum(a[u].rc,mid + 1,r,mid + 1,rr));
}
inline int query_rsum(int u,int l,int r,int ll,int rr){
if(rr < ll) return 0;
if(l == ll && r == rr) return a[u].rsum;
int mid = (l + r) >> 1;
if(rr <= mid) return query_rsum(a[u].lc,l,mid,ll,rr);
else if(ll > mid) return query_rsum(a[u].rc,mid + 1,r,ll,rr);
else return std::max(query_rsum(a[u].rc,mid + 1,r,mid + 1,rr),query_sum(a[u].rc,mid + 1,r,mid + 1,rr) +
query_rsum(a[u].lc,l,mid,ll,mid));
}
inline bool check(int mid,int Q1,int Q2,int Q3,int Q4){
int res = 0;
//printf("%d %d %d %d\n",Q1,Q2,Q3,Q4);
res += query_sum(rt[mid],1,n,Q2 + 1,Q3 - 1);
res += query_rsum(rt[mid],1,n,Q1,Q2);
res += query_lsum(rt[mid],1,n,Q3,Q4);
return res >= 0;
}
inline bool cmp(inf x,inf y){
return x.val < y.val;
}
int main(){
n = read();
for(int i = 1;i <= n;++i){
v[i].val = read();
v[i].pos = i;
}
std::sort(v + 1,v + n + 1,cmp);
build(rt[1],1,n);
for(int i = 2;i <= n + 1;++i) {
rt[i] = rt[i - 1];
ins(rt[i],1,n,v[i - 1].pos);
}
q = read();
LL last = 0,Q[4];
while(q--){
for(int i = 0;i < 4;++i) Q[i] = (read() + last ) % n + 1;
std::sort(Q,Q + 4);
int l = 1,r = n,ans;
while(l <= r){
int mid = (l + r) >> 1;//printf("%d\n",mid);
if(check(mid,Q[0],Q[1],Q[2],Q[3])) ans = mid,l = mid + 1;
else r = mid - 1;
}
printf("%lld\n",last = v[ans].val);
}
return 0;
}