题目
题目链接:https://www.luogu.com.cn/problem/P2839
一个长度为 (n) 的序列 (a),设其排过序之后为 (b),其中位数定义为 (b_{n/2}),其中 (a,b) 从 (0) 开始标号,除法取下整。
给你一个长度为 (n) 的序列 (s)。
回答 (Q) 个这样的询问:(s) 的左端点在 ([a,b]) 之间,右端点在 ([c,d]) 之间的子区间中,最大的中位数。
其中 (a<b<c<d)。
位置也从 (0) 开始标号。
我会使用一些方式强制你在线。
(n leq 20000),(Q leq 25000)。
思路
很有趣的一道题。
求区间 ([l,r]) 的中位数,可以二分答案 (mid),将大于等于 (mid) 的数字全部设为 (1),小于 (mid) 的数字全部设为 (-1),如果区间 ([l,r]) 的和大于 (0),那么答案就不小于 (mid),否则答案小于 (mid)。
本题求左端点在 ([l_1,r_1]),右端点在 ([l_2,r_2]) 中最大的中位数,发现 ([r_2,l_1]) 这一段是必须取的,然后前后各可以取一段前缀 / 后缀。
依然二分答案 (mid),转化为 (1,-1) 序列后,我们需要最大化左右两端前后缀的和使得总和尽量大,如果这个最大的和大于 (0),那么答案就超过 (mid),反之答案小于 (mid)。
那么可以先离散化,建立 (n) 棵线段树,第 (i) 棵线段树的区间 ([l,r]) 表示当 (mid=i) 时,区间 ([l,r]) 的和。接下来维护区间和、区间最大前缀 / 后缀和即可。
但是这样空间是 (O(n^2)) 的,发现第 (i) 棵线段树与第 (i-1) 棵线段树唯一有区别的地方就是离散化后数值为 (i-1) 的位置,总共的修改次数不会超过 (O(n)),所以用主席树即可。
时间复杂度 (O((n+m)log^2 n))。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=20010,LG=15,MAXN=N*LG*4;
int n,Q,tot,last,a[N],b[N],rt[N];
vector<int> pos[N];
struct SegTree
{
int tot,lc[MAXN],rc[MAXN],sum[MAXN],lmax[MAXN],rmax[MAXN];
void pushup(int x)
{
sum[x]=sum[lc[x]]+sum[rc[x]];
lmax[x]=max(lmax[lc[x]],sum[lc[x]]+lmax[rc[x]]);
rmax[x]=max(rmax[rc[x]],sum[rc[x]]+rmax[lc[x]]);
}
int build(int l,int r)
{
int x=++tot;
sum[x]=lmax[x]=rmax[x]=r-l+1;
if (l==r) return x;
int mid=(l+r)>>1;
lc[x]=build(l,mid);
rc[x]=build(mid+1,r);
return x;
}
int update(int x,int now,int l,int r,int k)
{
if (!x || x==now)
{
x=++tot;
lc[x]=lc[now]; rc[x]=rc[now];
sum[x]=sum[now]; lmax[x]=lmax[now]; rmax[x]=rmax[now];
}
if (l==k && r==k)
{
sum[x]=-1; lmax[x]=rmax[x]=0;
return x;
}
int mid=(l+r)>>1;
if (k<=mid) lc[x]=update(lc[x],lc[now],l,mid,k);
else rc[x]=update(rc[x],rc[now],mid+1,r,k);
pushup(x);
return x;
}
int query1(int x,int l,int r,int ql,int qr)
{
if (l==ql && r==qr) return sum[x];
int mid=(l+r)>>1;
if (qr<=mid) return query1(lc[x],l,mid,ql,qr);
if (ql>mid) return query1(rc[x],mid+1,r,ql,qr);
return query1(lc[x],l,mid,ql,mid)+query1(rc[x],mid+1,r,mid+1,qr);
}
int query2(int x,int l,int r,int ql,int qr)
{
if (l==ql && r==qr) return rmax[x];
int mid=(l+r)>>1;
if (qr<=mid) return query2(lc[x],l,mid,ql,qr);
if (ql>mid) return query2(rc[x],mid+1,r,ql,qr);
int sr=query1(rc[x],mid+1,r,mid+1,qr);
int mr=query2(rc[x],mid+1,r,mid+1,qr);
int ml=query2(lc[x],l,mid,ql,mid);
return max(mr,sr+ml);
}
int query3(int x,int l,int r,int ql,int qr)
{
if (l==ql && r==qr) return lmax[x];
int mid=(l+r)>>1;
if (qr<=mid) return query3(lc[x],l,mid,ql,qr);
if (ql>mid) return query3(rc[x],mid+1,r,ql,qr);
int sl=query1(lc[x],l,mid,ql,mid);
int ml=query3(lc[x],l,mid,ql,mid);
int mr=query3(rc[x],mid+1,r,mid+1,qr);
return max(ml,sl+mr);
}
}seg;
int binary(int l1,int r1,int l2,int r2)
{
int l=1,r=tot,mid;
while (l<=r)
{
mid=(l+r)>>1;
if (seg.query1(rt[mid],1,n,r1,l2)+seg.query2(rt[mid],1,n,l1,r1-1)+seg.query3(rt[mid],1,n,l2+1,r2)>=0) l=mid+1;
else r=mid-1;
}
return l-1;
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+1+n);
tot=unique(b+1,b+1+n)-b-1;
for (int i=1;i<=n;i++)
{
a[i]=lower_bound(b+1,b+1+tot,a[i])-b;
pos[a[i]].push_back(i);
}
tot++;
rt[0]=seg.build(1,n);
for (int i=1;i<=tot;i++)
{
rt[i]=rt[i-1];
for (int j=0;j<pos[i-1].size();j++)
rt[i]=seg.update(rt[i],rt[i-1],1,n,pos[i-1][j]);
}
scanf("%d",&Q);
while (Q--)
{
int p[5];
scanf("%d%d%d%d",&p[1],&p[2],&p[3],&p[4]);
p[1]=(p[1]+last)%n+1; p[2]=(p[2]+last)%n+1;
p[3]=(p[3]+last)%n+1; p[4]=(p[4]+last)%n+1;
sort(p+1,p+5);
printf("%d
",last=b[binary(p[1],p[2],p[3],p[4])]);
}
return 0;
}