题目描述
一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整。
给你一个长度为n的序列s。
回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。
其中a<b<c<d。
位置也从0开始标号。
我会使用一些方式强制你在线。
输入输出格式
输入格式:
第一行序列长度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]。
输入保证满足条件。
输出格式:
Q行依次给出询问的答案。
输入样例#1:
5
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0
输出样例#1:
271451044
271451044
969056313
说明
回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。 强制在线。
一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整。
给你一个长度为n的序列s。
回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。
其中a<b<c<d。
位置也从0开始标号。
我会使用一些方式强制你在线。
输入输出格式
输入格式:
第一行序列长度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]。
输入保证满足条件。
输出格式:
Q行依次给出询问的答案。
输入输出样例
5
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0
输出样例#1:
271451044
271451044
969056313
说明
暴力过不了
题意还是比较好理解的
嗯哼,现在让我们进入正题。
做完这题超爽♂
好吧,还是让我写一下题意
题意:给你一个长度为n的序列s。回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。 强制在线。
这一题对于一个我这个刚学主席树不到两天的人简直就是作死。
思路:首先考虑二分答案,判断可行的方法则是看是否大于他的数可以比小于他的数多。则考虑对于[b, c]直接求出来(因为不管怎么取都会取到这一段),对于[a, b-1]求最大的后缀(带有一点贪心思想),[c+1, d]求最大前缀,可以建线段树维护。如果对于每一个数建一个线段树,空间(嘿嘿嘿)就会爆了,所以我们考虑使用主席树维护。
怎么判断大于他的数可以比小于他的数多呢?
这里我们可以用一种比较巧妙的方法,我们把小于当前数的数全部标-1,大于等于的标1,到时只需要维护这个数组就可以了(是不是很棒棒)(手动滑稽)
总的复杂度 O(可以过(手动划去))显然可以(碾过)解决这道题。
下面请出我奇丑无比的代码:
#include<bits/stdc++.h> using namespace std; const int N=10000007; int a[N],ch[N][2],b[N],c[N],tot,n,m,root[N]; struct A{ int id,val; }; void push(int rt){ a[rt]=a[ch[rt][0]]+a[ch[rt][1]]; b[rt]=max(b[ch[rt][0]]+a[ch[rt][1]],b[ch[rt][1]]); //最大后缀 c[rt]=max(c[ch[rt][0]],c[ch[rt][1]]+a[ch[rt][0]]); //最大前缀 } void build(int l,int r,int rt){ //奇奇gay gay 的建树 if(l==r){ a[rt]=b[rt]=c[rt]=1; return; } int mid=(l+r)>>1; tot++; ch[rt][0]=tot; build(l,mid,tot); tot++; ch[rt][1]=tot; build(mid+1,r,tot); push(rt); } void insert(int l,int r,int x,int rt){ //奇奇gay gay 的插入♂ if(l==r){ tot++; a[tot]=b[tot]=c[tot]=-1; return; } int mid=(l+r)>>1; if(mid>=x){ insert(l,mid,x,ch[rt][0]); tot++; ch[tot][0]=tot-1; ch[tot][1]=ch[rt][1]; push(tot); }else{ insert(mid+1,r,x,ch[rt][1]); tot++; ch[tot][1]=tot-1; ch[tot][0]=ch[rt][0]; push(tot); } } int q[N],qq[N]; A p[N]; int query(int l,int r,int L,int R,int rt){ //区间和 if(L<=l&&r<=R) return a[rt]; int mid=(l+r)>>1,ret=0; if(L<=mid) ret+=query(l,mid,L,R,ch[rt][0]); if(R>mid) ret+=query(mid+1,r,L,R,ch[rt][1]); return ret; } int query1(int l,int r,int L,int R,int rt){ //最大后缀 if(L>R) return 0; if(L<=l&&r<=R) return b[rt]; int mid=(l+r)>>1,ret=0; if(L<=mid) ret=query1(l,mid,L,R,ch[rt][0]); if(R>mid) ret=max(ret+query(mid+1,r,L,R,ch[rt][1]),query1(mid+1,r,L,R,ch[rt][1])); return ret; } int query2(int l,int r,int L,int R,int rt){ //最大前缀 if(L>R) return 0; if(L<=l&&r<=R) return c[rt]; int mid=(l+r)>>1,ret=0; if(R>mid) ret=query2(mid+1,r,L,R,ch[rt][1]); if(L<=mid) ret=max(ret+query(l,mid,L,R,ch[rt][0]),query2(l,mid,L,R,ch[rt][0])); return ret; } int check(int l,int r,int ll,int rr){//毁天灭地的二分答案 int L=1,R=n+1,mid; while(L+1<R){ mid=(L+R)>>1; if(max(query1(1,n,l,r-1,root[mid-1]),0)+max(query2(1,n,ll+1,rr,root[mid-1]),0)+query(1,n,r,ll,root[mid-1])>=0) L=mid; else R=mid; } return L; } bool cmp(A x,A y){ return x.val<y.val; } int k,l,r,ll,rr,pp[5]; vector<int>pq[1000005]; int main(){ cin>>n; root[0]=0; build(1,n,root[0]); for(int i=1;i<=n;i++) scanf("%d",&p[i].val),q[i]=p[i].val,qq[i]=p[i].val,p[i].id=i; //中二的离散化 sort(p+1,p+1+n,cmp); for(int i=1;i<=n;i++){ insert(1,n,p[i].id,root[i-1]),root[i]=tot; } int last=0; cin>>m; while(m--){ scanf("%d%d%d%d",&l,&r,&ll,&rr); pp[0]=(l+last)%n;pp[1]=(r+last)%n;pp[2]=(ll+last)%n;pp[3]=(rr+last)%n; sort(pp,pp+4); last=check(pp[0]+1,pp[1]+1,pp[2]+1,pp[3]+1); last=p[last].val; printf("%d\n",last); last%=n; } }//一百零八行的结束(拜拜~)