[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; }
相关文章推荐
- 【bzoj2653】【middle】【主席树+二分答案】
- bzoj 2653: middle (二分+主席树)
- [BZOJ2653]middle(二分+主席树)
- BZOJ 2653: middle 主席树+二分
- 【BZOJ2653】【主席树+二分】middle
- 【BZOJ2653】middle(主席树,二分)
- [主席树+二分] BZOJ2653: middle
- bzoj2653 middle(主席树+二分)
- [BZOJ2653]middle(二分+主席树)
- BZOJ 2653 middle 二分+主席树
- [BZOJ2653]middle 主席树+二分
- bzoj2653 Middle 二分&主席树
- 【BZOJ2653】middle,主席树(非权值线段树)维护序列和信息+二分答案
- BZOJ2653 middle(二分答案+主席树)
- [bzoj2653][middle] (二分 + 主席树)
- 【bzoj2653】【middle】【主席树+二分答案】
- bzoj 2653 middle 二分答案 主席树判定
- Bzoj 2653 middle(二分+主席树)
- BZOJ2653 middle 【二分 + 主席树】
- [BZOJ2653]middle 主席树+二分答案