• bzoj 2653: middle


    Description

    一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整。给你一个
    长度为n的序列s。回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。
    其中a<b<c<d。位置也从0开始标号。我会使用一些方式强制你在线。

    Solution

    中位数的题一般是二分答案
    二分一个中位数(x),如果是中位数则比它小的数和比它大的数绝对值不超过1
    所以把大于(x)的数赋成1,小于(x)的数赋成-1,如果存在一个区间([l,r])使得区间和等于0就可以满足条件
    二分答案之后,要使得区间和尽量大
    所以维护一个([a,b])的最大后缀和,([c,d])的最大前缀和,再加上([b+1,c-1])的和就是最大区间的和
    所以我们只需要对每一个权值维护一个线段树就行了, 维护方法和区间子段和类似

    #include <bits/stdc++.h>
    using namespace std;
    const int N=20005;
    int n,a[N],rt[N],b[N],num,Q,ans=0,q[5],cnt=0,id[N];
    struct node{
    	int ls,rs;
    	int lsum,rsum,sum;
    }tr[N*30];
    inline void upd(int x){
    	int ls=tr[x].ls,rs=tr[x].rs;
    	tr[x].sum=tr[ls].sum+tr[rs].sum;
    	tr[x].lsum=max(tr[ls].lsum,tr[ls].sum+tr[rs].lsum);
    	tr[x].rsum=max(tr[rs].rsum,tr[rs].sum+tr[ls].rsum);
    }
    inline void build(int &x,int l,int r){
    	x=++cnt;
    	if(l==r){tr[x].lsum=tr[x].rsum=tr[x].sum=-1;return ;}
    	int mid=(l+r)>>1;
    	build(tr[x].ls,l,mid);build(tr[x].rs,mid+1,r);
    	upd(x);
    }
    inline void ins(int &x,int l,int r,int sa){
    	tr[++cnt]=tr[x];x=cnt;
    	if(l==r){tr[x].lsum=tr[x].rsum=tr[x].sum=1;return ;}
    	int mid=(l+r)>>1;
    	if(sa<=mid)ins(tr[x].ls,l,mid,sa);
    	else ins(tr[x].rs,mid+1,r,sa);
    	upd(x);
    }
    inline node qry(int x,int l,int r,int sa,int se){
    	if(sa<=l && r<=se)return tr[x];
    	int mid=(l+r)>>1;
    	if(se<=mid)return qry(tr[x].ls,l,mid,sa,se);
    	else if(sa>mid)return qry(tr[x].rs,mid+1,r,sa,se);
    	else{
    		node ls=qry(tr[x].ls,l,mid,sa,mid);
    		node rs=qry(tr[x].rs,mid+1,r,mid+1,se),ret;
    		ret.sum=ls.sum+rs.sum;
    		ret.lsum=max(ls.lsum,ls.sum+rs.lsum);
    		ret.rsum=max(rs.rsum,rs.sum+ls.rsum);
    		return ret;
    	}
    }
    inline bool check(int x){
    	node A,B,C;int sum=0;
    	if(q[1]+1<=q[2]-1)B=qry(rt[x],1,n,q[1]+1,q[2]-1),sum+=B.sum;
    	A=qry(rt[x],1,n,q[0],q[1]);sum+=A.rsum;
    	C=qry(rt[x],1,n,q[2],q[3]);sum+=C.lsum;
    	return sum>=0;
    }
    int main()
    {
    	freopen("pp.in","r",stdin);
    	freopen("pp.out","w",stdout);
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)scanf("%d",&a[i]),b[i]=a[i];
    	sort(b+1,b+n+1);
    	num=unique(b+1,b+n+1)-b-1;
    	for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+num+1,a[i])-b,id[a[i]]=i;
    	build(rt[n+1],1,n);
    	for(int i=n;i>=1;i--){
    		rt[i]=rt[i+1];
    		ins(rt[i],1,n,id[i]);
    	}
    	scanf("%d",&Q);
    	while(Q--){
    		for(int i=0;i<4;i++)scanf("%d",&q[i]),q[i]=(q[i]+ans)%n+1;
    		sort(q,q+4);
    		int l=1,r=num,mid,ret=0;
    		while(l<=r){
    			mid=(l+r)>>1;
    			if(check(mid))ret=mid,l=mid+1;
    			else r=mid-1;
    		}
    		printf("%d
    ",ans=b[ret]);
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    无线安全课堂:手把手教会你搭建伪AP接入点
    转载——开阔自己的视野,勇敢的接触新知识
    关于系统架构的一些总结
    MessageBox.Show()如何换行
    不患寡而患不均
    由CHAR(2)引发的BUG
    DataRow.RowState 属性
    C# 使用TimeSpan计算两个时间差
    利用反射调出其他项目的界面
    DB2 中将date类型的转换成timestamp
  • 原文地址:https://www.cnblogs.com/Yuzao/p/8421542.html
Copyright © 2020-2023  润新知