description
给定一个序列 sss,qqq次询问,每次询问所有左端点在 [a,b][a,b][a,b] ,右端点在 [c,d][c,d][c,d] 里面的区间的中位数的最大值,强制在线
subtask 1:n,q≤100subtask\ 1:n,q\leq 100subtask 1:n,q≤100
subtask 2:n≤2000,m≤2.5×104subtask\ 2:n\leq 2000,m\leq 2.5\times 10^4subtask 2:n≤2000,m≤2.5×104
subtask 3:n,q≤2.5×104subtask\ 3:n,q\leq 2.5\times 10^4subtask 3:n,q≤2.5×104
solution
subtask 1:subtask\ 1:subtask 1:
瞎暴力即可
subtask 2:subtask\ 2:subtask 2:
我们可以先预处理出任意的[l,r][l,r][l,r]的中位数,用一个二维线段树之类的东西储存,每次查询相当于是平面上一个矩形求最大值
subtask 3:subtask\ 3:subtask 3:
一个数能不能是区间的中位数的判定方法可以把所有≥\geq≥这个数的都变成111,其他的变成−1-1−1,每次查询区间和就可以判定
我们考虑二分答案,对于每一个权值建立可持久化线段树,每次check的时候,相当于看存不存在一个左端点在[a,b][a,b][a,b]内,右端点在[c,d][c,d][c,d]的区间,使得这个区间的和≥0\geq 0≥0
其实这个东西本质上是sum[b,c]+sufmax[a,b)+premax(c,d]sum[b,c]+sufmax[a,b)+premax(c,d]sum[b,c]+sufmax[a,b)+premax(c,d]
所以我们用可持久化线段树维护区间和,区间前缀最大值,区间后缀最大值就可以啦
时间复杂度O(qlog2n)O(q\log^2n)O(qlog2n)
空间复杂度O(nlogn)O(n\log n)O(nlogn)
#include <bits/stdc++.h>
using namespace std;
# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)
typedef long long ll;
const int N=1e5+5;
template<typename T> void read(T &x){
x=0;int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
x*=f;
}
int n,q;
int head[N],cnt;
int b[N],sz;
int root[N],tot;
int in[5],ans;
struct misaka{
int val,id;
bool operator < (const misaka &cmp)const{
return val<cmp.val;
}
}a[N];
struct mikoto{
int lc,rc;
int lmax,rmax,sum;
}seg[N*30];
mikoto merge(mikoto l,mikoto r){
mikoto res;
res.lmax=max(l.lmax,l.sum+r.lmax);
res.rmax=max(r.rmax,r.sum+l.rmax);
res.sum=l.sum+r.sum;
return res;
}
void pushup(int u){
seg[u].lmax=max(seg[seg[u].lc].lmax,seg[seg[u].lc].sum+seg[seg[u].rc].lmax);
seg[u].rmax=max(seg[seg[u].rc].rmax,seg[seg[u].rc].sum+seg[seg[u].lc].rmax);
seg[u].sum=seg[seg[u].lc].sum+seg[seg[u].rc].sum;
}
int build(int l,int r){
int u=++tot;
if(l==r){
seg[u].lmax=seg[u].rmax=0;
seg[u].sum=-1;
return u;
}
int mid=l+r>>1;
seg[u].lc=build(l,mid);
seg[u].rc=build(mid+1,r);
pushup(u);
return u;
}
int update(int o,int l,int r,int x,int k){
int u=++tot;
seg[u]=seg[o];
if(l==r){
seg[u].sum=seg[u].lmax=seg[u].rmax=k;
return u;
}
int mid=l+r>>1;
if(x<=mid)seg[u].lc=update(seg[u].lc,l,mid,x,k);
else seg[u].rc=update(seg[u].rc,mid+1,r,x,k);
pushup(u);
return u;
}
mikoto query(int u,int l,int r,int ql,int qr){
if(l>=ql&&r<=qr)return seg[u];
int mid=l+r>>1;
if(qr<=mid)return query(seg[u].lc,l,mid,ql,qr);
if(ql>mid)return query(seg[u].rc,mid+1,r,ql,qr);
return merge(query(seg[u].lc,l,mid,ql,qr),query(seg[u].rc,mid+1,r,ql,qr));
}
bool check(int mid){
int res=0;
if(in[1]<=in[2]-1)res+=query(root[mid],1,n,in[1],in[2]-1).rmax;
res+=query(root[mid],1,n,in[2],in[3]).sum;
if(in[3]+1<=in[4])res+=query(root[mid],1,n,in[3]+1,in[4]).lmax;
return res>=0;
}
int main()
{
read(n);
Rep(i,1,n)read(a[i].val),a[i].id=i,b[i]=a[i].val;
sort(b+1,b+n+1);
sz=unique(b+1,b+n+1)-b-1;
Rep(i,1,n)a[i].val=lower_bound(b+1,b+sz+1,a[i].val)-b;
sort(a+1,a+n+1);
root[sz+1]=build(1,n);
int now=n;
_Rep(i,sz,1){
root[i]=root[i+1];
while(now&&a[now].val==i)root[i]=update(root[i],1,n,a[now].id,1),now--;
}
read(q);
while(q--){
Rep(i,1,4)read(in[i]);
Rep(i,1,4)in[i]=(in[i]+ans)%n+1;
sort(in+1,in+4+1);
int l=1,r=sz,res=0;
while(l<=r){
int mid=l+r>>1;
if(check(mid))res=mid,l=mid+1;
else r=mid-1;
}
ans=b[res];
printf("%d\n",ans);
}
return 0;
}

本文介绍了一种解决在线区间中位数查询问题的方法,通过使用二维线段树和可持久化线段树来预处理和查询任意区间的中位数,实现高效的时间复杂度O(qlog²n)和空间复杂度O(nlogn)。
461

被折叠的 条评论
为什么被折叠?



