题解:洛谷 P2893([国家集训队] middle)

Link

1. Description

给定一个长度为 \(n\) 的序列和 \(q\) 个询问 \((a,b,c,d)\),表示询问所有 \(l\in[a,b],r\in[c,d]\)\(a\) 的子序列中位数的最大值,这里的中位数在序列长度为偶数时取较大的那个不是中间两个的平均值。

2. Solution

看到中位数,我们本能地想到二分,\(\operatorname{check}\) 很好想,我们将二分出来的值设为 \(x\),那么序列中大于等于 \(x\) 的值视为 \(1\),其他值视为 \(-1\),然后找到 \(l\in[a,b],r\in[c,d]\) 的所有子序列中区间和最大的哪一个是不是一个非负整数即可。

找到最大的区间和可以进行一个转化,首先所有子序列必然包含 \([b+1,c-1]\) 这一段,所以先加上这一段的区间和,然后贪心地取 \([a,b]\) 这个子序列中以 \(b\) 为起点的最大子段和和 \([c,d]\) 这个子序列中以 \(c\) 为起点的最大子段和,不能不取,就可以得到最大的区间和。

但是,这个做法有一个很大的问题,我们如果直接朴素且暴力地预处理出所有情况下的序列 \(a\),时空复杂度为 \(O(n^2)\),显然这是无法接受的,但是我们发现一个显然的结论:如果从小到大地枚举 \(x\),那么所有值只可能从 \(1\) 变成 \(-1\)。所以我们使用线段树维护每一个位置的权值,同时维护每一个区间的区间和,以左、右端点为起点的最大子段和,然后进行可持久化维护历史版本即可。

最后的时间复杂度为 \(O(q\log^2 n)\),空间复杂度为 \(O(n\log n)\),显然足够通过此题了。

3. Code

/*by qwer6*/
/*略去缺省源与快读快写*/
const int N=2e4+5;
int n,q,tot;
int a[N],tmp[N],rt[N],p[4];
vector<int>place[N];
struct Node{
	int lmx,rmx,sum;
	Node(int _lmx=0,int _rmx=0,int _sum=0){
		lmx=_lmx,rmx=_rmx,sum=_sum;
	}
	Node friend operator +(Node a,Node b){
		Node res;
		res.sum=a.sum+b.sum;
		res.lmx=max(a.lmx,a.sum+b.lmx);
		res.rmx=max(b.rmx,b.sum+a.rmx);
		return res;
	}
};
struct Segment_tree{
	int num;
	Node c[N<<5];
	int ls[N<<5],rs[N<<5];
	#define mid (l+r>>1)
	int New(){
		num++;
		ls[num]=rs[num]=0;
		c[num]=Node(0,0,0);
		return num;
	}
	int copy(int p){
		int q=New();
		ls[q]=ls[p],rs[q]=rs[p],c[q]=c[p];
		return q;
	}
	void pushup(int p){
		c[p]=c[ls[p]]+c[rs[p]];
	}
	int build(int l,int r){
		int p=New();
		if(l==r){
			c[p]=Node(1,1,1);
			return p;
		}
		ls[p]=build(l,mid),rs[p]=build(mid+1,r);
		pushup(p);
		return p;
	}
	int change(int p,int l,int r,int x,int v){
		int q=copy(p);
		if(l==r){
			c[q]=Node(v,v,v);
			return q;
		}
		if(mid>=x)ls[q]=change(ls[p],l,mid,x,v);
		else rs[q]=change(rs[p],mid+1,r,x,v);
		pushup(q);
		return q;
	}
	Node query(int p,int l,int r,int L,int R){
		if(L<=l&&r<=R)return c[p];
		if(mid>=L&&mid<R)return query(ls[p],l,mid,L,R)+query(rs[p],mid+1,r,L,R);
		if(mid>=L)return query(ls[p],l,mid,L,R);
		return query(rs[p],mid+1,r,L,R);
	}
	#undef mid
}Set;
bool check(int l1,int r1,int l2,int r2,int x){
	int sum=0;
	if(r1+1<=l2-1)sum+=Set.query(rt[x],1,n,r1+1,l2-1).sum;
	sum+=Set.query(rt[x],1,n,l1,r1).rmx;
	sum+=Set.query(rt[x],1,n,l2,r2).lmx;
	return sum>=0;
}
signed main(){
	read(n);
	for(int i=1;i<=n;i++)tmp[i]=read(a[i]);
	sort(tmp+1,tmp+n+1);
	tot=unique(tmp+1,tmp+n+1)-tmp-1;
	for(int i=1;i<=n;i++){
		a[i]=lower_bound(tmp+1,tmp+tot+1,a[i])-tmp;
		place[a[i]].push_back(i);
	}
	rt[1]=Set.build(1,n);
	for(int i=2;i<=tot;i++){
		rt[i]=rt[i-1];
		for(int x:place[i-1])
			rt[i]=Set.change(rt[i],1,n,x,-1);
	}	
	read(q);
	for(int i=1,last=0;i<=q;i++){
		p[0]=(last+read())%n+1;
		p[1]=(last+read())%n+1;
		p[2]=(last+read())%n+1;
		p[3]=(last+read())%n+1;
		sort(p,p+4);
		int l=1,r=tot,ans=-1;
		while(l<=r){
			int mid=l+r>>1;
			if(check(p[0],p[1],p[2],p[3],mid)){
				ans=mid;
				l=mid+1;
			}else r=mid-1;
		}
		last=tmp[ans];
		write(last),Nxt;
	}
}
posted @ 2025-04-12 21:29  陈牧九  阅读(39)  评论(0)    收藏  举报