几个数据结构问题

时间:2021-01-20 11:15:43

题目1描述:给定n个数字,数字是[1,c]的数字,给定m个区间询问(n<=300000,c<=300000,m<=100000)
问是否存在>(r-l+1)/2的数字


分析:我们先来看这样的两个例子
奇数长度的区间 ******* 这里我们将区间内的数字排了个序 
                       [      ]
                              [     ]
注意到没,如果存在那样一个数,那么至少是比这两个区间大的区间滑动
可以发现无论这个区间是怎么样的,一定包含第(r-l+1)/2+1小的数 可以用鸽巢定理来解释
于是假定存在这样一个数x,那么必定是第(r-l+1)/2+1小的在这个区间,同理偶数长度
这个问题我们可以在logn内获得答案利用划分树
接下来我们怎么确定存不存在呢,我们把这个子问题转化一下变成
对于给定的一个x在区间[l,r]内出现的次数
我们可以建立c个平衡树每颗树保存该值所有的位置,来获取区间内的和
当然也可以直接二分来获得,那么总的复杂度为nlogn+mlogn 空间复杂度为nlogn+c

 

 

题目2描述:给定一个长为n的序列,找k个互不相同的区间,且区间长度在[L,R]内(n,k<=500000)
使得这k个区间的和最大

做法1:是枚举所有区间排序,取前面k个大的区间和,复杂度n*n*logn

做法2:对于一个区间[i,j]的和,我们可以用s[i]-s[j-1]来表示
设dp[i]=s[i]-min(s[j]) (L<=i-j<=R 即 i-R<=j<=i-L)表示以i结尾的最大区间和
我们显然我们肯定是先找区间和最大的,然后区间和次大的,直到找出k个,
那么这k个中,潜在的存在着这些dp[i],当然不一定是全部(为什么后面会讲到)
于是我们可以用一个堆来维护这样的三元组(i,j,k),表示以i结尾的第k大的区间和所对应的j
因为对于一个i而言,所有的范围内的j,可能有多个都比较大,所以每次操作完一个[j,i]区间后
我们必须能够快速的直到下一个大的j,即寻找区间内的第k大问题,这个可以利用划分树
在logn时间内获得,然后再把这个新的三元组插入到堆中
于是这个问题就可以在nlogn时间内解决了

做法3:根据此题,本质上来说是寻找这样的若干个区间,我们假定还是以i结尾,那么j值呢是在
某个区间[L,R]内的,对于一个i,我们取得区间内的最值,显然这个j不能再用了,于是它吧区间
分裂成[L,j-1] [j+1,R],那么又是新的三元组,对于i的维护,我们还是利用堆来完成i的选取
整体复杂度为nlogn,空间复杂度为nlogn

其中区间最值查询可以用RMQ预处理出来,至此问题圆满完成

 

题目3描述:给定n个数字,组成序列,有m个操作,操作1:指定区间内[l,r]所有元素+det (n,m<=100000)

操作2:询问指定区间内的最长连续递增序列

分析:如果只有操作1,那么我们很容易利用线段树来维护区间的和,只需要在区间上加入增量V

然而对于询问,我们需要处理子区间的合并,于是我们需要三个变量lm,rm,mm,这段区间从左开始的最长连续递增

到右结束的最长连续递增,整大段区间的连续最长递增,然而对于区间的合并,我们还需要知道端点处具体的值

所以还需要区间边界上真实的值,通常来说我们利用懒惰标记,当需要时才更新来达到logn的维护级别,因为区间最多

下沉logn次,上浮logn次,可以用down和up来描述,对于任何一种操作,我们始终先down后up,down时更新当前

区间的左右真实值,同时将V下传,在下降过程中,V始终是真实的,所以对于未来up时,两个子区间的边界是真实值

于是要传递给父亲,这样保证了来回一次传递后边界的正确性,其中lm,rm,mm的维护在up中操作

这样我们就可以在logn时间内完成修改和查询,总的复杂度为nlogn