您的位置:首页 > 其它

bzoj2653 Middle 二分&主席树

2016-02-12 12:12 399 查看
这题厉害啊。。。根本想不到。(据说是clj的题?)

首先要想到二分答案,想到就解出一半了。假设有一个值x,如果x是区间[l,r]的中位数,且l∈[a,b],r∈[c,d],那么答案一定>=x;否则答案一定<x。

那么如何判断x是否是[l,r]的中位数呢?实际上,如果使用二分的话,我们不需要知道x是否一定是[l,r]的中位数,只要知道[l,r]中位数是否>=x即可。我们首先看一下如何判断任意区间[u,v]的中位数是否>=x。

对于所有的数,如果我们将>=x的数令为1,<x的数令为-1,将这个新的数列称为基于x的新数列。那么如果在某一个区间[u,v]中,新的数列的和>=0,那么[u,v]的中位数一定>=x。那么,如果我们要知道是否存在[l,r],且l∈[a,b],r∈[c,d],[l,r]的中位数>=x,只需要知道是否存在[l,r],l∈[a,b],r∈[c,d],[l,r]的新的数列的和>=0即可。换句话说,我们只需要知道l∈[a,b],r∈[c,d]时[l,r]的最大子段和。这个最大子段和显然可以用线段树维护,只需要得到任意区间[u,v]的左连续最大子段和,右连续最大子段和,以及区间和即可。这样,答案相当于[a,b]的右连续最大子段和+[c,d]的左连续最大子段和+[b+1,c-1]的区间和。

所以关键是如何构造出这样的线段树,因为x的数量太多了,为O(N),因此不可能构建N颗基于x的新的数列的线段树。但是关键的是这些线段树的大小形态是完全相同的,所以可以用主席树的方式做。讲到这差不多应该知道怎么做了吧。

首先,这里线段树是以a[i]的值构造的,因此我们首先将a[i]排序。首先构造最小的a[i]的线段树,我们构造一颗满的线段树。显然所有的数都>=a[i],因此将基于a[i]的新的数列全部赋为1构造线段树。

现在假设我们在构造第k小的a[x],而且第k-1小的是a[y]。注意到相对于基于a[y]的,在基于a[x]的新的数列中,只有第y个数从1变成了-1。但是万一a[y]=a[x]怎么办?没关系你会发现询问的结果是一样的。因此我们只需要将第y个数变成-1,因为只变了1个数,所以只有logN个节点发生了变化,其余节点一个指针过去就行了。

那么查询就很简单了。只需要判断x对应的线段树[a,b]的右连续最大子段和+[c,d]的左连续最大子段和+[b+1,c-1]的区间和是否>=0即可。

AC代码如下(还是比较简单的才2k):

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200005
#define M 8000005
using namespace std;

struct node{ int x,y,sum; }a
,val[M];
int n,m,trtot,rt
,ls[M],rs[M];
void maintain(int k){
int l=ls[k],r=rs[k]; val[k].sum=val[l].sum+val[r].sum;
val[k].x=max(val[l].x,val[l].sum+val[r].x);
val[k].y=max(val[r].y,val[l].y+val[r].sum);
}
void build(int &k,int l,int r){
k=++trtot; if (l==r){ val[k].x=val[k].y=val[k].sum=1; return; }
int mid=(l+r)>>1; build(ls[k],l,mid); build(rs[k],mid+1,r);
maintain(k);
}
void ins(int l,int r,int x,int &y,int k,int v){
y=++trtot; int mid=(l+r)>>1;
if (l==r){ val[y].x=val[y].y=val[y].sum=v; return; }
if (k<=mid){ rs[y]=rs[x]; ins(l,mid,ls[x],ls[y],k,v); }
else{ ls[y]=ls[x]; ins(mid+1,r,rs[x],rs[y],k,v); }
maintain(y);
}
node qry(int k,int l,int r,int x,int y){
if (l==x && r==y) return val[k]; int mid=(l+r)>>1;
if (y<=mid) return qry(ls[k],l,mid,x,y); else
if (x>mid) return qry(rs[k],mid+1,r,x,y); else{
node t1=qry(ls[k],l,mid,x,mid),t2=qry(rs[k],mid+1,r,mid+1,y);
t1.x=max(t1.x,t1.sum+t2.x); t1.y=max(t2.y,t1.y+t2.sum);
t1.sum+=t2.sum; return t1;
}
}
bool cmp(node aa,node bb){ return aa.x<bb.x; }
bool ok(int x,int l1,int l2,int r1,int r2){
int tmp=0,k=rt[x]; if (l2+1<r1) tmp=qry(k,1,n,l2+1,r1-1).sum;
return tmp+qry(k,1,n,l1,l2).y+qry(k,1,n,r1,r2).x>=0;
}
int main(){
scanf("%d",&n); int i;
for (i=1; i<=n; i++){ scanf("%d",&a[i].x); a[i].y=i; }
sort(a+1,a+n+1,cmp); build(rt[1],1,n);
for (i=2; i<=n; i++) ins(1,n,rt[i-1],rt[i],a[i-1].y,-1);
scanf("%d",&m); int ans=0,ask[4],q[4];
while (m--){
for (i=0; i<4; i++){ scanf("%d",&ask[i]); ask[i]=(ask[i]+ans)%n; }
sort(ask,ask+4); for (i=0; i<4; i++) q[i]=ask[i]+1;
int l=1,r=n+1; while (l+1<r){ int mid=(l+r)>>1; if (ok(mid,q[0],q[1],q[2],q[3])) l=mid; else r=mid; }
printf("%d\n",ans=a[l].x);
}
return 0;
}


by lych
2016.2.12


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: