BZOJ2653 middle 【主席树】【二分】*

BZOJ2653 middle


Description

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

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]。  
输入保证满足条件。
第一行所谓“排过序”指的是从小到大排序!
n<=20000,Q<=25000

Output

Q行依次给出询问的答案。

Sample Input

5
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0

Sample Output

271451044
271451044
969056313


这应该算是一道思路题,首先如果对于一个数我们确定了它的值x我们应该怎么做??首先,我们发现所有小于x的数对x是不是中位数的贡献是等价的,同理所有大于x的数对x是不是中位数的贡献是等价的,那么我们可以把所有大于x的数赋值为1,把所有小于x的数赋值为-1,然后我们就可以利用这里的一个性质:当一个区间的最大子段和大于等于0,x一定可以在某种情况下成为中位数,至于为什么。。首先我们发现随着x增大,区间的最大子段和是单调递减的,所以当区间最大子段和大于等于0的时候,当前节点合法,并且显然可能有比它更大的数成为中位数

然后我们考虑怎么对这个区间最大字段和进行维护,首先b~c的节点是必须要选的,我们就维护sum就可以了,然后对于a到b的区间和c到d的区间我们考虑维护最大右子段和和最大左子段和,这些操作都可以在线段树上进行实现,很简单,就不详细说了

那么显然我们是不可能二分每个数然后暴力修改成1和-1的,所以我们考虑将权值作为一个维度,随着权值增加我们修改1的值为-1,这个操作我们可以用主席树实现,我们就根据权值大小来建立主席树,每一棵线段树中以在原序列中的位置作为下标进行维护,然后更新一下,就做完了啦


以前只觉得主席树可以维护一下区间第K大这样板子问题,不知道还可以利用可持久化的性质根据权值的大小来建树,涨姿势了


#include<bits/stdc++.h>
using namespace std;
#define N 20010
#define M 400010
int n,m,cnt=0,tot=0,lastans=0;
int x[N],pre[N],q[5];
int root[N],ls[M],rs[M],sum[M],lsum[M],rsum[M];
vector<
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值