【题解】P2839 [国家集训队] middle

0

首先我们要思考如何判断最大中位数是多少

即二分一个数 \(mid\),把 \(>=mid\) 的赋值为 \(1\) ,否则为 \(-1\) ,求最大区间值

如果 \(>=0\) ,则 \(mid\) 偏小,否则偏大,继续二分

1

对于如何快速求最大区间,最大前后缀

我们有一个想法,离散化,对于每一个权值 \(x\) 建立一颗线段树,这个线段树节点关于 \(x\) 赋值

然后维护线段树信息

但是暴力建树,时空都会爆炸,我们可以通过可持久化的方式优化时空

2:代码

#include<bits/stdc++.h>
#define inf 1e9
using namespace std;
const int N=2e4+10;
struct node{
	int l,r;
	int sum,maxl,maxr;
	int son[2];
	#define l(q) tree[q].l
	#define r(q) tree[q].r
	#define sum(q) tree[q].sum
	#define maxl(q) tree[q].maxl
	#define maxr(q) tree[q].maxr
	#define ls(q) tree[q].son[0]
	#define rs(q) tree[q].son[1]
	void show(int q){
		//cout<<"-->"<<q<<" "<<l<<" "<<r<<" "<<sum<<" "<<maxl<<" "<<maxr<<" "<<son[0]<<" "<<son[1]<<"\n";
	}
	void init(){
		sum=0;
		maxl=-inf;maxr=-inf;
	}
}tree[N*25];int idx=0;
int root[N];
int id[N];
int a[N];
bool cmp(int x,int y){
	return a[x]<a[y];
}
void push_up(int q){
	sum(q)=sum(ls(q))+sum(rs(q));
	maxl(q)=max(maxl(ls(q)),sum(ls(q))+maxl(rs(q)));
	maxr(q)=max(maxr(rs(q)),sum(rs(q))+maxr(ls(q)));
}
node merge(node lx,node rx){
	node c;
	c.sum=lx.sum+rx.sum;
	c.maxl=max(lx.maxl,lx.sum+rx.maxl);
	c.maxr=max(rx.maxr,rx.sum+lx.maxr);
	return c;
}
int build(int l,int r){
	int q=++idx;
	l(q)=l;r(q)=r;
	sum(q)=maxl(q)=maxr(q)=r-l+1;
	if(l==r) return q;
	
	int mid=l+r>>1;
	ls(q)=build(l,mid);
	rs(q)=build(mid+1,r);
	
	return q;
}
void update(int pre,int &now,int tp,int d){
	now=++idx;
	int q=now;
	tree[q]=tree[pre];
	
	int l=l(q),r=r(q);
	if(l==r){
		sum(q)=maxl(q)=maxr(q)=-1;
		if(d==2) tree[q].show(q);
		return ;
	}
	int mid=l+r>>1;
	if(tp<=mid) update(ls(pre),ls(q),tp,d);
	else update(rs(pre),rs(q),tp,d);
	
	push_up(q);
	if(d==2) tree[q].show(q);
}
node ans;
void query(int q,int L,int R){
	int l=l(q),r=r(q);
	if(L<=l&&r<=R){
		//cout<<":";tree[q].show(q);
		ans=merge(ans,tree[q]);
		//cout<<l<<" "<<r<<" "<<ans.sum<<" "<<ans.maxl<<" "<<ans.maxr<<"\n";
		//cout<<":";tree[q].show(q);
		return ;
	}
	int mid=l+r>>1;
	if(L<=mid) query(ls(q),L,R);
	if(mid<R) query(rs(q),L,R);
	//cout<<l<<" "<<r<<" "<<ans.sum<<" "<<ans.maxl<<" "<<ans.maxr<<"\n";
	return ;
}
bool check(int x,int a,int b,int c,int d){
	int res=0;
	//cout<<x<<" ";
	ans.init();query(root[x],a,b);res+=ans.maxr;//cout<<ans.maxr<<" ";
	if(b+1<=c-1){
		ans.init();
		query(root[x],b+1,c-1);
		res+=ans.sum;
		//cout<<ans.sum<<" ";
	}
	ans.init();query(root[x],c,d);res+=ans.maxl;
	///cout<<ans.maxl<<" ";
	//cout<<res<<"\n";
	return res>=0;
}
void dfs(int q){
	tree[q].show(q);
	int l=l(q),r=r(q);
	if(l==r) return ;
	dfs(ls(q));dfs(rs(q));
}
int main(){
	int n;cin>>n;
	for(int i=0;i<n;i++){
		cin>>a[i];
		id[i]=i;
	}
	sort(id,id+n,cmp);
	
	
	root[0]=build(0,n-1);
	for(int i=1;i<n;i++){
		update(root[i-1],root[i],id[i-1],i);
	}
	//cout<<"----------->\n";
	dfs(root[2]);
	//cout<<"sigma";tree[3].show(3);
	int m;cin>>m;
	int ans=0;
	while(m--){
		int q[4];
		for(int i=0;i<4;i++){
			cin>>q[i];
			q[i]=(q[i]+ans)%n;
		}
		sort(q,q+4);
		//for(int i=0;i<4;i++) cout<<q[i]<<" ";cout<<"\n";
		int l=0,r=n-1;
		while(l<r){
			int mid=l+r+1>>1;
			if(check(mid,q[0],q[1],q[2],q[3])) l=mid;
			else r=mid-1;
		}
		cout<<a[id[l]]<<"\n";
		ans=a[id[l]];
	}
	///cout<<"sigma";tree[3].show(3);
	//cout<<"----->\n";
	//check(2,0,1,2,3);
	return 0;
}

3:反思

3.1

最大中位数求解方式,可以先通过二分下手,转成判定问题

3.2

主席树也可以关于权值建立线段树

posted @ 2026-06-29 20:48  Aistyr  阅读(3)  评论(0)    收藏  举报