您的位置:首页 > 其它

[BZOJ2653]middle(主席树+二分)

2017-04-03 21:08 483 查看

=== ===

这里放传送门

=== ===

题解

这道题最关键的一点就是想到一个对于备选答案的科学判定方法。

给定一个数字v,如何判断这个区间的中位数和这个数字的关系?

如果把这个区间内大于这个数字的位置赋值为1,小于这个数字的位置赋值为-1,那么如果这段区间的和小于0,就说明这个区间的中位数比v要小;如果区间和等于0,就说明中位数恰好是v;如果区间和大于0,就说明中位数比v要大。

这说明每次判定一个答案就可以筛掉一部分肯定不合法的答案。那么我们可以二分这个中位数,然后把序列上小于它的数赋值为-1,大于的赋值为1,然后只要能构造出区间和大于等于0的区间就说明答案合法。

我们肯定不能每次二分都重新赋值。但是可以注意到当待判定的答案变大的时候,1的数量单调减小而-1的数量单调增加。并且每个数变成-1以后就不会再变了,一共只会改变n次。那么我们可以用以权值为root数组下标,内层线段树按位置维护的主席树来存储这个序列,每次只需要把恰好从1变成-1的位置修改过来,其余的继承前面的结果就可以了。

查询的话只需要在线段树中维护区间和,靠左的最大和还有靠右的最大和就可以了。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 1000000000
using namespace std;
int n,tmp[20010],p[20010],v[20010],q,root[20010],cnt,size,anti[20010],lastans;
struct segtree{
int lMax,rMax,sum,l,r;
segtree(){lMax=rMax=-inf;sum=l=r=0;}
void count();
}t[2000010];
void segtree::count(){
sum=t[l].sum+t[r].sum;
lMax=max(t[l].lMax,t[l].sum+t[r].lMax);
rMax=max(t[r].rMax,t[r].sum+t[l].rMax);
}
int comp(int x,int y){return tmp[x]<tmp[y];}
void build(int &i,int l,int r){
i=++size;t[i]=segtree();
if (l==r){
t[i].lMax=t[i].rMax=t[i].sum=1;
return;
}
int mid=(l+r)>>1;
build(t[i].l,l,mid);
build(t[i].r,mid+1,r);
t[i].count();
}
void insert(int &i,int j,int l,int r,int x,int fir){
if (fir==-1||i<fir){i=++size;t[i]=t[j];}
if (l==r){
t[i].lMax=t[i].rMax=t[i].sum=-1;
return;
}
int mid=(l+r)>>1;
if (x<=mid) insert(t[i].l,t[j].l,l,mid,x,fir);
else insert(t[i].r,t[j].r,mid+1,r,x,fir);
t[i].count();
}
segtree query(int i,int l,int r,int left,int right){
if (i==0||left>right) return segtree();
if (left<=l&&right>=r) return t[i];
int mid=(l+r)>>1;
segtree L=segtree(),R=segtree();
if (left<=mid) L=query(t[i].l,l,mid,left,right);
if (right>mid) R=query(t[i].r,mid+1,r,left,right);
if (L.lMax==-inf) return R;
if (R.lMax==-inf) return L;
L.lMax=max(L.lMax,L.sum+R.lMax);
L.rMax=max(R.rMax,R.sum+L.rMax);
L.sum+=R.sum;
return L;
}
bool check(int val,int a,int b,int c,int d){
segtree L,R,S;
L=query(root[val-1],1,n,a,b);
R=query(root[val-1],1,n,c,d);
S=query(root[val-1],1,n,b+1,c-1);
return (L.rMax+S.sum+R.lMax)>=0;
}
int divide(int l,int r,int a,int b,int c,int d){
int mid,ans=0;
while (l<=r){
mid=(l+r)>>1;
if (check(tmp[mid],a,b,c,d)==true){
ans=max(ans,mid);l=mid+1;
}else r=mid-1;
}
return tmp[ans];
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++){
scanf("%d",&tmp[i]);p[i]=i;
}
sort(p+1,p+n+1,comp);
tmp[0]=-inf;
for (int i=1;i<=n;i++){
if (tmp[p[i]]==tmp[p[i-1]])
v[p[i]]=cnt;
else v[p[i]]=++cnt;
anti[cnt]=tmp[p[i]];
}
for (int i=1;i<=n;i++) tmp[i]=v[i];
sort(tmp+1,tmp+n+1);
memset(root,-1,sizeof(root));
build(root[0],1,n);
for (int i=1;i<=n;i++)
insert(root[tmp[i]],root[tmp[i]-1],1,n,p[i],root[tmp[i]]);
scanf("%d",&q);
cnt=unique(tmp+1,tmp+n+1)-tmp-1;
for (int i=1;i<=q;i++){
int Q[5];
scanf("%d%d%d%d",&Q[1],&Q[2],&Q[3],&Q[4]);
for (int j=1;j<=4;j++) Q[j]=(Q[j]+lastans)%n+1;
sort(Q+1,Q+5);
lastans=anti[divide(1,cnt,Q[1],Q[2],Q[3],Q[4])];
printf("%d\n",lastans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  BZOJ 二分 主席树