BZOJ 2653: middle(二分+主席树)

Description

一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整。给你一个

长度为n的序列s。回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。

其中a<b<c<d。位置也从0开始标号。我会使用一些方式强制你在线。

 

Input

第一行序列长度n。接下来n行按顺序给出a中的数。

接下来一行Q。然后Q行每行a,b,c,d,我们令上个询问的答案是

x(如果这是第一个询问则x=0)。

令数组q={(a+x)%n,(b+x)%n,(c+x)%n,(d+x)%n}。

将q从小到大排序之后,令真正的

要询问的a=q[0],b=q[1],c=q[2],d=q[3]。  

输入保证满足条件。

第一行所谓“排过序”指的是从小到大排序!

n<=20000,Q<=25000

 

Output

Q行依次给出询问的答案。

思路:
对于每次询问,二分一个答案,判断是否可行。若大于等于这个数字
的位置标为1,否则标为0,即查询a~b的右连续子区间加上b+1~c-1 的整段区间,在加上c~d的左连续区间的最大值
是否大于等于0。可以用主席树维护每一个状态的0和1的信息。
代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=20005;
struct node
{
	int v,id;
}c[maxn];
int L[maxn*20],R[maxn*20],sum[maxn*20],ls[maxn*20],rs[maxn*20];
bool cmp(const node &a,const node &b) {return a.v<b.v;}
int tol,n,T[maxn],p[5];
void push_up(int rt)
{
	sum[rt]=sum[L[rt]]+sum[R[rt]];
	ls[rt]=max(ls[L[rt]],sum[L[rt]]+ls[R[rt]]);
	rs[rt]=max(rs[R[rt]],sum[R[rt]]+rs[L[rt]]);
}
int build(int l,int r)
{
	int rt=++tol;
	if(l==r)
	{
		sum[rt]=ls[rt]=rs[rt]=1;
		return rt;
	}
	int mid=(l+r)/2;
	L[rt]=build(l,mid);
	R[rt]=build(mid+1,r);
	push_up(rt);
	return rt;
}
int update(int pre,int l,int r,int pos)
{
	int rt=++tol;
	L[rt]=L[pre];R[rt]=R[pre];
	if(l==r)
	{
		sum[rt]=ls[rt]=rs[rt]=-1;
		return rt;
	}
	int mid=(l+r)/2;
	if(pos<=mid) L[rt]=update(L[pre],l,mid,pos);
	else R[rt]=update(R[pre],mid+1,r,pos);
	push_up(rt);
	return rt;
}
int query1(int rt,int ll,int rr,int l,int r)
{
	if(l>=ll&&r<=rr) return sum[rt];
	int mid=(l+r)/2;
	int ans=0;
	if(ll<=mid) ans+=query1(L[rt],ll,rr,l,mid);
	if(rr>mid) ans+=query1(R[rt],ll,rr,mid+1,r);
	return ans;
}
int query2(int rt,int ll,int rr,int l,int r)
{
	if(l>=ll&&r<=rr) return ls[rt];
	int mid=(l+r)/2;
	if(ll<=mid&&rr>mid) return max(query2(L[rt],ll,rr,l,mid),query1(L[rt],ll,rr,l,mid)+query2(R[rt],ll,rr,mid+1,r));
	if(ll<=mid) return query2(L[rt],ll,rr,l,mid);
	if(rr>mid) query2(R[rt],ll,rr,mid+1,r);
}
int query3(int rt,int ll,int rr,int l,int r)
{
	if(l>=ll&&r<=rr) return rs[rt];
	int mid=(l+r)/2;
	if(ll<=mid&&rr>mid) return max(query3(R[rt],ll,rr,mid+1,r),query1(R[rt],ll,rr,mid+1,r)+query3(L[rt],ll,rr,l,mid));
	if(ll<=mid) return query3(L[rt],ll,rr,l,mid);
	if(rr>mid) return query3(R[rt],ll,rr,mid+1,r);
}
bool check(int x,int a,int b,int c,int d)
{
	int ans=0;//a=p[0],b=p[1],c=p[2],d=p[3];
	if(b+1<c)ans+=query1(T[x],b+1,c-1,1,n);
	ans+=query2(T[x],c,d,1,n);
	ans+=query3(T[x],a,b,1,n);
	return ans>=0;
}
int main()
{
	int Q;scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&c[i].v);
		c[i].id=i;
	}
	sort(c+1,c+n+1,cmp);
	T[1]=build(1,n);
	for(int i=2;i<=n;i++)
		T[i]=update(T[i-1],1,n,c[i-1].id);
	int x=0;
	scanf("%d",&Q);
	while(Q--)
	{
		int x0,x1,x2,x3;scanf("%d%d%d%d",&x0,&x1,&x2,&x3);
		p[0]=(x0+x)%n+1;p[1]=(x1+x)%n+1;p[2]=(x2+x)%n+1;p[3]=(x3+x)%n+1;
		sort(p,p+4);
		int l=1,r=n,i;
		while(l<=r)
		{
			int mid=(l+r)/2;
			if(check(mid,p[0],p[1],p[2],p[3])) i=mid,l=mid+1;
			else r=mid-1;
		}
		printf("%d\n",x=c[i].v);
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值