【洛谷P2839】middle(二分答案)(主席树)

传送门


题解:

复习一下常见的trick。

求中位数转化为二分答案,大于等于的部分设置成 1 1 1 小的部分设置成 − 1 -1 1然后求和,看结果是否大于等于 0 0 0 来判断是否可行。

这道题直接按照权值排序,以原序列标号为下标建立主席树,叶节点权值为在当前树中它应该为的权值,对于询问,中间的询问和,两边的询问最大前后缀即可。


代码:

#include
#define ll long long
#define re register
#define cs const

using std::cerr;
using std::cout;

cs int N=2e4+7;

namespace PST{

cs int N=::N*40;

struct atom{int lm,rm,sm;}t[N];
atom operator+(cs atom &a,cs atom &b){
	return (atom){
		std::max(a.lm,a.sm+b.lm),
		std::max(b.rm,b.sm+a.rm),
		a.sm+b.sm};
}
int lc[N],rc[N],tot;

void _copy(int &u){
	int v=++tot;t[v]=t[u],lc[v]=lc[u],rc[v]=rc[u],u=v;
}

void build(int &u,int l,int r){
	u=++tot;if(l==r){
		t[u]={1,1,1};
		return ;
	}int m=(l+r)>>1;
	build(lc[u],l,m);build(rc[u],m+1,r);
	t[u]=t[lc[u]]+t[rc[u]];
}

void ins(int &u,int l,int r,int p){
	_copy(u);if(l==r){
		t[u]={-1,-1,-1};
		return ;
	}int m=(l+r)>>1;
	p<=m?ins(lc[u],l,m,p):ins(rc[u],m+1,r,p);
	t[u]=t[lc[u]]+t[rc[u]];
}

atom query(int u,int l,int r,int ql,int qr){
	if((ql<=l&&r<=qr)||!u)return t[u];int m=(l+r)>>1;
	if(qr<=m)return query(lc[u],l,m,ql,qr);
	if(m<ql)return query(rc[u],m+1,r,ql,qr);
	return query(lc[u],l,m,ql,qr)+query(rc[u],m+1,r,ql,qr);
}

}

int rt[N];

int n,a[N],b[N],bn;
std::vector<int> ps[N];

void Main(){
	scanf("%d",&n);
	for(int re i=0;i<n;++i)
		scanf("%d",a+i),b[i]=a[i];
	std::sort(b,b+n);
	bn=std::unique(b,b+n)-b;
	for(int i=0;i<n;++i)
		ps[std::lower_bound(b,b+bn,a[i])-b].push_back(i);
	PST::build(rt[0],0,n-1);
	for(int re i=1;i<bn;++i){
		rt[i]=rt[i-1];
		for(int re v:ps[i-1])
			PST::ins(rt[i],0,n-1,v);
	}int Q,ans=0;scanf("%d",&Q);
	while(Q--){
		static int t[4];
		for(int i=0;i<4;++i)
			scanf("%d",t+i),t[i]=(t[i]+ans)%n;
		std::sort(t,t+4);
		int a=t[0],b=t[1],c=t[2],d=t[3];
		int l=0,r=bn-1;
		while(l<=r){
			int m=(l+r)>>1;
			int val=
				PST::query(rt[m],0,n-1,a,b).rm+
				PST::query(rt[m],0,n-1,c,d).lm+
				((b+1<c)?PST::query(rt[m],0,n-1,b+1,c-1).sm:0);
			if(val>=0)ans=::b[m],l=m+1;
			else r=m-1;
		}cout<<ans<<"\n";
	}
}

inline void file(){
#ifdef zxyoi
	freopen("middle.in","r",stdin);
#endif
}signed main(){file();Main();return 0;}

你可能感兴趣的:(二分答案,线段树)