BZOJ2653:middle——题解
http://www.lydsy.com/JudgeOnline/problem.php?id=2653
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
5
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0
271451044
969056313
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0
Sample Output
271451044271451044
969056313
——————————————————————————
debug真爽……一会再讲我神奇的debug经历。
(因为参照代码太多了所以就不一一贴了)
我们考虑如何求一个数比中位数大还是小。
方法很简单:将小于该数的数一律变成-1,大于等于的变为1,求和,如果和>=0即*可能为*这个数。
也就是说,我们可以用这个方法二分答案来求得中位数。
那么我们对于一组询问a,b,c,d,判断x与中位数的大小关系,就可以是求最大子序列和的过程了。
那么我们按照元素下标建线段树,并且存储每个区间的最大左/右连续和,,每个区间的和。
那么答案就是(ab最大右连续和)+(bc和)+(cd最大左连续和)。
但是我们不可能为每一组询问重新开一棵线段树,所以我们需要一种*预处理*所有可能的线段树的方法。
于是我们想到了主席树。
我们先对数列排序,这样假设我们中位数下标(以下都是排序后的新下标)为x,则显然0~x-1都是-1,而x~n-1都是1
那么我们的建树过程无非就是第一棵树全是1,而后的树对于前面的树的值都更新为-1就可以了。
(看着复杂,代码也复杂,debug1h一无所获,最后我精简代码发现我i和j搞反了……)
#include<cstdio> #include<queue> #include<cctype> #include<cstring> #include<cmath> #include<vector> #include<algorithm> using namespace std; const int N=20010; inline int read(){ int X=0,w=0;char ch=0; while(!isdigit(ch)){w|=ch=='-';ch=getchar();} while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); return w?-X:X; } struct tree{ int l,r,sum,lx,rx; }tr[N*20]; struct num{ int v,p; }a[N]; int rt[N],n,m,q,pool,p[4]; bool cmp(num f,num s){return f.v<s.v;} inline void update(int x){ tr[x].sum=tr[tr[x].l].sum+tr[tr[x].r].sum; tr[x].lx=max(tr[tr[x].l].lx,tr[tr[x].l].sum+tr[tr[x].r].lx); tr[x].rx=max(tr[tr[x].r].rx,tr[tr[x].r].sum+tr[tr[x].l].rx); return; } inline void build(int &x,int l,int r){ x=++pool; if(l==r){ tr[x].sum=tr[x].lx=tr[x].rx=1; return; } int mid=(l+r)>>1; build(tr[x].l,l,mid); build(tr[x].r,mid+1,r); update(x); return; } inline void insert(int y,int &x,int l,int r,int p,int v){ tr[x=++pool]=tr[y]; if(l==r){ tr[x].sum=tr[x].lx=tr[x].rx=v; return; } int mid=(l+r)>>1; if(p<=mid)insert(tr[y].l,tr[x].l,l,mid,p,v); else insert(tr[y].r,tr[x].r,mid+1,r,p,v); update(x); return; } inline int query_all(int k,int l,int r,int l1,int r1){ if(l==l1&&r==r1)return tr[k].sum; int mid=(l+r)>>1; if(r1<=mid) return query_all(tr[k].l,l,mid,l1,r1); else if(l1>mid) return query_all(tr[k].r,mid+1,r,l1,r1); else return query_all(tr[k].l,l,mid,l1,mid)+query_all(tr[k].r,mid+1,r,mid+1,r1); } inline int query_l(int k,int l,int r,int l1,int r1){ if(l==l1&&r==r1)return tr[k].lx; int mid=(l+r)>>1; if(r1<=mid) return query_l(tr[k].l,l,mid,l1,r1); else if(l1>mid) return query_l(tr[k].r,mid+1,r,l1,r1); else return max(query_l(tr[k].l,l,mid,l1,mid),query_all(tr[k].l,l,mid,l1,mid)+query_l(tr[k].r,mid+1,r,mid+1,r1)); } inline int query_r(int k,int l,int r,int l1,int r1){ if(l==l1&&r==r1)return tr[k].rx; int mid=(l+r)>>1; if(r1<=mid) return query_r(tr[k].l,l,mid,l1,r1); else if(l1>mid) return query_r(tr[k].r,mid+1,r,l1,r1); else return max(query_r(tr[k].r,mid+1,r,mid+1,r1),query_all(tr[k].r,mid+1,r,mid+1,r1)+query_r(tr[k].l,l,mid,l1,mid)); } bool pan(int k){ int sum=0; if(p[1]+1<p[2])sum+=query_all(rt[k],0,n-1,p[1]+1,p[2]-1); sum+=query_r(rt[k],0,n-1,p[0],p[1]); sum+=query_l(rt[k],0,n-1,p[2],p[3]); return sum>=0; } int erfen(){ int l=0,r=n-1,ans; while(l<=r){ int mid=(l+r)>>1; if(pan(mid)){ ans=a[mid].v; l=mid+1; }else r=mid-1; } return ans; } int main(){ n=read(); for(int i=0;i<n;i++){ a[i].v=read(); a[i].p=i; } sort(a,a+n,cmp); build(rt[0],0,n-1); for(int i=1;i<n;i++)insert(rt[i-1],rt[i],0,n-1,a[i-1].p,-1); q=read(); int pre=0; for(int i=1;i<=q;i++){ for(int j=0;j<4;j++)p[j]=(read()+pre)%n; sort(p,p+4); printf("%d\n",pre=erfen()); } return 0; }