[国家集训队]Middle

description

给定一个序列 s s s q q q次询问,每次询问所有左端点在 [ a , b ] [a,b] [a,b] ,右端点在 [ c , d ] [c,d] [c,d] 里面的区间的中位数的最大值,强制在线

s u b t a s k   1 : n , q ≤ 100 subtask\ 1:n,q\leq 100 subtask 1:n,q100
s u b t a s k   2 : n ≤ 2000 , m ≤ 2.5 × 1 0 4 subtask\ 2:n\leq 2000,m\leq 2.5\times 10^4 subtask 2:n2000,m2.5×104
s u b t a s k   3 : n , q ≤ 2.5 × 1 0 4 subtask\ 3:n,q\leq 2.5\times 10^4 subtask 3:n,q2.5×104

solution

s u b t a s k   1 : subtask\ 1: subtask 1:
瞎暴力即可
s u b t a s k   2 : subtask\ 2: subtask 2:
我们可以先预处理出任意的 [ l , r ] [l,r] [l,r]的中位数,用一个二维线段树之类的东西储存,每次查询相当于是平面上一个矩形求最大值
s u b t a s k   3 : subtask\ 3: subtask 3:
一个数能不能是区间的中位数的判定方法可以把所有 ≥ \geq 这个数的都变成 1 1 1,其他的变成 − 1 -1 1,每次查询区间和就可以判定

我们考虑二分答案,对于每一个权值建立可持久化线段树,每次check的时候,相当于看存不存在一个左端点在 [ a , b ] [a,b] [a,b]内,右端点在 [ c , d ] [c,d] [c,d]的区间,使得这个区间的和 ≥ 0 \geq 0 0

其实这个东西本质上是 s u m [ b , c ] + s u f m a x [ a , b ) + p r e m a x ( c , d ] sum[b,c]+sufmax[a,b)+premax(c,d] sum[b,c]+sufmax[a,b)+premax(c,d]

所以我们用可持久化线段树维护区间和,区间前缀最大值,区间后缀最大值就可以啦

时间复杂度 O ( q log ⁡ 2 n ) O(q\log^2n) O(qlog2n)

空间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

#include 
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;
}

你可能感兴趣的:(#,线段树,#,可持久化)