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

2653: middle

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 2252  Solved: 1252
[Submit][Status][Discuss]

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
271451044
969056313

Sample Output

 

HINT

 

  0:n,Q<=100

1,...,5:n<=2000

0,...,19:n<=20000,Q<=25000


 

 

Source

 
[ Submit][ Status][ Discuss]

关于中位数的题一般首先二分答案,然后根据大于此数和小于此数的个数判断,这题中如果大于等于此数的数-小于此数的数得到的结果大于0则说明中位数可能更大。

换句话说就是所有大于等于的位置赋为1,小于赋为-1,则一段区间和非负。

但是每次二分都要全部赋值一次吗?不需要,因为考虑每相邻的两个权值从1变到-1的数是不多的,用主席树先全部建好就可以了。

需要同时维护三个值:lmax,rmax,sum,发现合并lmax和rmax的时候需要sum,那么这个复杂度还能保证吗?答案是肯定的,因为最多每个点的常数*2,复杂度是不会变的。当然也可以每次将三个点放在一起合并,复杂度不变。

最后,这题第一行的排序是指从小到大排序的。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define lson ls[x],L,mid
 4 #define rson rs[x],mid+1,R
 5 #define rep(i,l,r) for (int i=l; i<=r; i++)
 6 using namespace std;
 7 
 8 const int N=5000100;
 9 struct P{
10     int sm,lmx,rmx; P(){};
11     P(int _sm,int _lmx,int _rmx):sm(_sm),lmx(_lmx),rmx(_rmx){}
12 }seg[N];
13 int ls[N],rs[N],T[30010],q[4],ans,nd,n,Q;
14 struct A{
15     int x,y;
16     bool operator <(const A &a)const{ return (x==a.x) ? y<a.y : x<a.x; }
17 }a[30010];
18 
19 P operator +(const P &a,const P &b){ return P(a.sm+b.sm,max(a.lmx,a.sm+b.lmx),max(b.rmx,b.sm+a.rmx)); }
20 
21 void build(int &x,int L,int R){
22     x=++nd;
23     if (L==R){ seg[x]=P(1,1,1); return; }
24     int mid=(L+R)>>1; build(lson); build(rson);
25     seg[x]=seg[ls[x]]+seg[rs[x]];
26 }
27 
28 void mdf(int y,int &x,int L,int R,int pos){
29     x=++nd; ls[x]=ls[y]; rs[x]=rs[y];
30     if (L==R){ seg[x]=P(-1,-1,-1); return; }
31     int mid=(L+R)>>1;
32     if (pos<=mid) mdf(ls[y],lson,pos); else mdf(rs[y],rson,pos);
33     seg[x]=seg[ls[x]]+seg[rs[x]];
34 }
35 
36 P ask(int x,int L,int R,int l,int r){
37     if (l>r) return P(0,0,0);
38     if (L==l && r==R) return seg[x];
39     int mid=(L+R)>>1;
40     if (r<=mid) return ask(lson,l,r);
41     else if (l>mid) return ask(rson,l,r);
42         else return ask(lson,l,mid)+ask(rson,mid+1,r);
43 }
44 
45 int jud(int k){ return ask(T[k],1,n,q[0],q[1]).rmx+ask(T[k],1,n,q[1]+1,q[2]-1).sm+ask(T[k],1,n,q[2],q[3]).lmx>=0; }
46 
47 int main(){
48     freopen("bzoj2653.in","r",stdin);
49     freopen("bzoj2653.out","w",stdout);
50     scanf("%d",&n);
51     rep(i,1,n) scanf("%d",&a[a[i].y=i].x);
52     sort(a+1,a+n+1); build(T[1],1,n);
53     rep(i,2,n) mdf(T[i-1],T[i],1,n,a[i-1].y);
54     scanf("%d",&Q);
55     while (Q--){
56         scanf("%d%d%d%d",&q[0],&q[1],&q[2],&q[3]);
57         q[0]=(q[0]+ans)%n+1; q[1]=(q[1]+ans)%n+1;
58         q[2]=(q[2]+ans)%n+1; q[3]=(q[3]+ans)%n+1; sort(q,q+4);
59         int L=1,R=n;
60         while (L<=R){
61             int mid=(L+R)>>1;
62             if (jud(mid)) L=mid+1; else R=mid-1;
63         }
64         printf("%d\n",ans=a[L-1].x);
65     }
66     return 0;
67 }

 

转载于:https://www.cnblogs.com/HocRiser/p/8987648.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值