您的位置:首页 > 其它

BZOJ 2653 middle

2016-06-17 11:52 183 查看
title: ‘BZOJ 2653 middle’

categories: BZOJ

date: 2016-1-6 19:30:00

tags: [主席树,二分]

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]。

输入保证满足条件。

Output

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

Sample

input.txt

5

170337785

271451044

22430280

969056313

206452321

3

3 1 0 2

2 3 1 4

3 1 4 0

output.txt

271451044

271451044

969056313

Hint

0:n,Q<=100

1,…,5:n<=2000

0,…,19:n<=20000,Q<=25000

Solution

这道题真的挺难想的= =

我想了好久没想出来,然后又看题解看了好久,毕竟太菜。。。(orz神犇zyj随意秒了,无情d我)

要求中位数最大,而且区间端点要在两个给定的区间内,我们考虑二分这个中位数,然后转化成判定问题。

那么怎么判定某段区间内某个数x能不能作为中位数?

对于区间内的数,凡是>=x的数都标为1,否则标为-1,然后求出这个区间的最大子段和。若sum>=0,则该数肯定可以成为这个区间的中位数。求最大子段和,可以通过在线段树里维护一下lmax和rmax来求得,类似于里的操作。由于要求两端点分别在[a,b],[c,d]中,因此最大子段和就是T[a,b]rmax+T[b+1,c−1]sum+T[c,d]lmax。

线段树不可能建很多棵,主席树是很好的解决方案。我们发现大小相邻的两个数的线段树只相差一处地方,于是可以先对数字排序,然后按大小依次建树,初始所有的点都为1,然后依次修改为-1即可。

还有一个问题,要是我们二分出的中位数不在我们选中的区间内怎么办?

若二分出的数不在区间内且判定成功了,就说明区间内绝对有一个更大的符合要求的数,可以直接继续向上二分。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>

#define maxn 20000+5
#define maxp 4000000+5

using namespace std;

struct Ha_Tree{
int l,r;
int lx,rx,sum;
}tr[maxp];

int root[maxn],ori[maxn],pos[maxn];
int n,m,ind;

bool cmp(int s,int b){
return ori[s]<ori[b];
}

void Update(int k){
int l=tr[k].l,r=tr[k].r;
tr[k].sum=tr[l].sum+tr[r].sum;
tr[k].lx=max(tr[l].lx,tr[l].sum+tr[r].lx);
tr[k].rx=max(tr[r].rx,tr[r].sum+tr[l].rx);
}

void Modify(int l,int r,int last,int &k,int pos,int val){
k=++ind;
tr[k]=tr[last];
if(l==r){
tr[k].sum=tr[k].lx=tr[k].rx=val;
return;
}
int mid=(l+r)>>1;
if(pos<=mid) Modify(l,mid,tr[last].l,tr[k].l,pos,val);
else Modify(mid+1,r,tr[last].r,tr[k].r,pos,val);
Update(k);
}

int Query_all(int k,int l,int r,int pl,int pr){
if(l==pl && r==pr) return tr[k].sum;
int mid=(l+r)>>1;
if(pr<=mid) return Query_all(tr[k].l,l,mid,pl,pr);
else if(pl>mid) return Query_all(tr[k].r,mid+1,r,pl,pr);
else return Query_all(tr[k].l,l,mid,pl,mid)+Query_all(tr[k].r,mid+1,r,mid+1,pr);
}

int Query_l(int k,int l,int r,int pl,int pr){
if(l==pl && r==pr) return tr[k].lx;
int mid=(l+r)>>1;
if(pr<=mid) return Query_l(tr[k].l,l,mid,pl,pr);
else if(pl>mid) return Query_l(tr[k].r,mid+1,r,pl,pr);
else return max(Query_l(tr[k].l,l,mid,pl,mid),Query_all(tr[k].l,l,mid,pl,mid)+Query_l(tr[k].r,mid+1,r,mid+1,pr));
}

int Query_r(int k,int l,int r,int pl,int pr){
if(l==pl && r==pr) return tr[k].rx;
int mid=(l+r)>>1;
if(pr<=mid) return Query_r(tr[k].l,l,mid,pl,pr);
else if(pl>mid) return Query_r(tr[k].r,mid+1,r,pl,pr);
else return max(Query_r(tr[k].r,mid+1,r,mid+1,pr),Query_all(tr[k].r,mid+1,r,mid+1,pr)+Query_r(tr[k].l,l,mid,pl,mid));
}

bool check(int k,int a,int b,int c,int d){
int res=0;
if(b+1!=c) res+=Query_all(root[k],1,n,b+1,c-1);
res+=Query_r(root[k],1,n,a,b);
res+=Query_l(root[k],1,n,c,d);
if(res>=0) return true;
return false;
}

int find(int a,int b,int c,int d){
int l=1,r=n;
while(l<r){
int mid=(l+r+1)>>1;
if(check(mid,a,b,c,d)) l=mid;
else r=mid-1;
}
return ori[pos[l]];
}

void Build(int l,int r,int &k){
k=++ind;
if(l==r){
tr[k].lx=tr[k].rx=tr[k].sum=1;
return;
}
int mid=(l+r)>>1;
Build(l,mid,tr[k].l);
Build(mid+1,r,tr[k].r);
Update(k);
}

int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&ori[i]),pos[i]=i;
sort(pos+1,pos+1+n,cmp);
Build(1,n,root[1]);
for(int i=2;i<=n;i++)
Modify(1,n,root[i-1],root[i],pos[i-1],-1);
scanf("%d",&m);
int x=0;
for(int i=1;i<=m;i++){
int q[4];
scanf("%d%d%d%d",&q[0],&q[1],&q[2],&q[3]);
for(int i=0;i<4;i++) q[i]=(q[i]+x)%n+1;
sort(q,q+4);
printf("%d\n",x=find(q[0],q[1],q[2],q[3]));
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  主席树 二分