【主席树+二分】middle

本文探讨了一种巧妙的二分查找题目解法,通过将数值转换为-1和1来判断mid值与正确答案的关系,并使用主席树进行高效区间查询,实现对中位数的有效判断。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

 

很妙的一道题。二分答案的检验很妙妙。

如何检验mid比答案大了还是小了?

把大等于mid的数都变成1,小于mid的数都变成-1。在一个区间内,如果一个数是中位数,那么区间和就是0(偶数个数)或者1(奇数个数)。

对于一个区间与二分的答案mid,

如果这个区间的总和等于0,那mid就是中位数了。

如果这个区间的总和大于0,那么在这个区间内一定会存在一个大于mid的数,使得区间总和变小。因为如果mid变大了,那么-1就会增多,1就会减少。——这就说明mid比答案小了。

反之,如果总和小于0,那么一定存在一个小于mid的数使得区间总和变大。

那么对于左端点在[a,b],右端点在[c,d]的这个区间,[b+1,c-1]是必须要取的,只需要看[a,b]的最大右缀和与[c,d]的最大左缀和。

如果rans(a,b)+sum(b+1,c-1)+lans(c,d)≥0,就放大答案,

反之缩小答案。

 

现在问题就变成了如何维护每个数对应出来的序列(全部变成-1和1)。

用主席树维护。这里的线段树对应的区间代表的是下标。(root[i]表示从小到大排第i个的元素的线段树的根。)

先把所有数从小到大排序。那么root[0]就可以把所有数都赋成1,因为它是第0小的(最小的)。

inline void build(int &root,int l,int r){
	root=++tot;
	if(l==r){T[root].lans=T[root].rans=T[root].sum=1;return;}
	build(ls,l,mid),build(rs,mid+1,r);
	pushup(root);
}

这样就把第0个树建好了。

考虑root[i]和root[i-1]之间的关系。root[i]只用在root[i-1]的基础上把第(i-1)小的那个位置改成-1就行了。想一想大小关系就好。

递归的时候pushup一下就改好了。

inline void insert(int &root,int pre,int l,int r,int pos){
	T[root=++tot]=T[pre];
	if(l==r){T[root].lans=T[root].rans=T[root].sum=-1;return;}
	if(pos<=mid) insert(ls,T[pre].lc,l,mid,pos);
	else insert(rs,T[pre].rc,mid+1,r,pos);
	pushup(root);
}

然后询问的时候合并一下线段树节点就好

struct node{
	int lans,rans,sum,lc,rc;
	node(){lans=rans=sum=0;}
	friend inline node operator+(const node &pl,const node &pr){
		node ret;
		ret.lans=max(pl.lans,pl.sum+pr.lans);
		ret.rans=max(pr.rans,pr.sum+pl.rans);
		ret.sum=pl.sum+pr.sum;
		return ret;
	}
}T[maxn*40];
inline node query(int root,int l,int r,int x,int y){
	if(l>=x&&r<=y) return T[root];
	if(y<=mid) return query(ls,l,mid,x,y);
	if(x> mid) return query(rs,mid+1,r,x,y);
	return query(ls,l,mid,x,mid)+query(rs,mid+1,r,mid+1,y);
}

 check函数写的时候不要把lans和rans搞混了。

inline bool check(int k,int a,int b,int c,int d,int SUM=0){
	if(b+1<=c-1)SUM+=query(root[k],0,n-1,b+1,c-1).sum;
	SUM+=query(root[k],0,n-1,a,b).rans;
	SUM+=query(root[k],0,n-1,c,d).lans;
	return SUM>=0;
}

 

 

写主席树的时候想好各个参数的含义!(0,n-1)是原序列下标区间。a[i].id是排序后第i大的元素在原序列中的下标。

 

全部代码:

#include<bits/stdc++.h>
#define mid ((l+r)>>1)
#define ls T[root].lc
#define rs T[root].rc
using namespace std;
const int maxn=2e4+10;
int n,m,q,Q[4],root[maxn],lastans=0,tot=0,P,l,r;
struct point{int val,id;}a[maxn];
struct node{
	int lans,rans,sum,lc,rc;
	node(){lans=rans=sum=0;}
	friend inline node operator+(const node &pl,const node &pr){
		node ret;
		ret.lans=max(pl.lans,pl.sum+pr.lans);
		ret.rans=max(pr.rans,pr.sum+pl.rans);
		ret.sum=pl.sum+pr.sum;
		return ret;
	}
}T[maxn*40];
bool cmp(point x,point y){return x.val<y.val;}
inline int read(){
	int x=0;char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return x;
}
inline void print(int x){
	if(x>9) print(x/10);
	putchar(x%10+'0');
}
inline void pushup(int root){
	T[root].sum=T[ls].sum+T[rs].sum;
	T[root].lans=max(T[ls].lans,T[ls].sum+T[rs].lans);
	T[root].rans=max(T[rs].rans,T[rs].sum+T[ls].rans);
}
inline void build(int &root,int l,int r){
	root=++tot;
	if(l==r){T[root].lans=T[root].rans=T[root].sum=1;return;}
	build(ls,l,mid),build(rs,mid+1,r);
	pushup(root);
}
inline void insert(int &root,int pre,int l,int r,int pos){
	T[root=++tot]=T[pre];
	if(l==r){T[root].lans=T[root].rans=T[root].sum=-1;return;}
	if(pos<=mid) insert(ls,T[pre].lc,l,mid,pos);
	else insert(rs,T[pre].rc,mid+1,r,pos);
	pushup(root);
}
inline node query(int root,int l,int r,int x,int y){
	if(l>=x&&r<=y) return T[root];
	if(y<=mid) return query(ls,l,mid,x,y);
	if(x> mid) return query(rs,mid+1,r,x,y);
	return query(ls,l,mid,x,mid)+query(rs,mid+1,r,mid+1,y);
}
inline bool check(int k,int a,int b,int c,int d,int SUM=0){
	if(b+1<=c-1)SUM+=query(root[k],0,n-1,b+1,c-1).sum;
	SUM+=query(root[k],0,n-1,a,b).rans;
	SUM+=query(root[k],0,n-1,c,d).lans;
	return SUM>=0;
}
int main(){
	n=read();
	for(int i=0;i<n;++i)a[i].val=read(),a[i].id=i;
	sort(a,a+n,cmp),build(root[0],0,n-1);
	for(int i=1;i<n;++i) insert(root[i],root[i-1],0,n-1,a[i-1].id);
	q=read();
	while(q--){
		for(int i=0;i<4;++i) Q[i]=(read()+lastans)%n;
		sort(Q,Q+4),l=0,r=n-1;
		while(l<=r)
		if(check(mid,Q[0],Q[1],Q[2],Q[3])) P=mid,l=mid+1;
		else r=mid-1;
		lastans=a[P].val,print(lastans),putchar('\n');
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值