数字序列

先将题目给出的b转化为单调不降序列。具体来说,对题目给出的原序列a,每个位置都减去其下标得到a;对任意一种构造的b,也都减去其下标得到b,显然i=1n|aibi|=i=1n|(ai)(bii)|=i=1n|aibi|;下文都以a,b指代a,b,并设c[i][j]表示a中的i ~ j排好序之后的数组(也就是说c是一个三维数组)

问题一:如果知道了对于a,最好的构造的b都是相同的(即b1=b2=...=bn),那么b的值应该为什么?

答:显然为货仓选址问题,ba的中位数(如果n为偶数,任取一个数都可以)

问题二:如果知道了对于a,存在一个下标k,使得1 ~ k的最佳构造的b相同(i.e.当b1=b2=...=bk时,i=1k|aibi|最小)且k+1 ~ n的最佳构造的b相同(i.e.当bk+1=bk+2=...=bn 时,i=k+1n|aibi|最小),是否可以构造出整个a的最佳构造,如果可以,如何构造?

答:可以构造出来。不妨设b1=v1,bk+1=v2

如果说v1v2,那么整个a的最佳构造b就是b1=b2=...=bk=v1,bk+1=...=bn=v2,这是因为i=1n|aibi|=i=1k|aibi|+i=k+1n|aibi|,两者刚好取到下界且满足b单调不降

如果说v1>v2,考虑一个最优构造b,如果说bk>v1,我们将b的前k个数全部变成v1,显然满足单调不降而且答案不会更差;否则的话,此时已经有bkv1,如果此时还有bk+1<v2,我们将bk+1,bk+2,...,bn全部改成v2,显然满足单调不降且答案不会更差。也就是说一定存在一组最优解,使得bkv1,bk+1v2。接下来考虑b的前k个数。假设b现在长成这个样子:

image

我们将除了最后一段b的剩下的b整体向上移动到倒数第二段b与最后一段b相同,如下

image

此过程答案一定不会更差。这是因为:设(移动前)最后一段b的下标为p+1 ~ k,那么对于前p个数来说,c[1][p]p2+1(也就是a的前p个数的中位数,想一下为什么c的下标一定是p2+1而不能是其他的)一定不会低于v1(否则的话b的前p个数的取值不会是v1,而是c[1][p]p2+1,这样答案肯定严格更优秀,就与我们的假设矛盾了),也就是说a的前p个数至少有p2个数不低于v1,而上面的移动让b的前p个数更加接近v1,于是i=1k|aibi|增加的值一定不会超过减少的值。推而广之,最后我们可以将b的前p个数变成相同的值,为bk。此时我们不再考虑bk后面的b,只考虑b的前k个数(也就是暂时先不管整个b的单调不降),将这k个数往v1整体移动,显然答案也不会变差

对于bk后面的b,我们也可以类似地证明,可以将所有的b都变成bk+1并且可以再整体往v2靠近;这样的话,我们就可以最终将b整体变成一样的值(注意最开始有bkbk+1),并且答案不会更差

也就是说,对于整个a来说,存在一个最优解b使得所有b的值相同;由于当b全部取a的中位数的时候,是在满足“b的值都相同”的前提下,答案最小的解,所以这就是在满足“b的值都相同”的前提下的下界,而我们肯定可以达到这个下界,于是构造完毕

有了上面两个问题做铺垫,我们来考虑原问题。注意原问题是满足子问题最优性的。

假设现在已经构造好了前i个数的最优解,对于ai+1,如果ai+1bi,那么bi+1=ai+1,从而找到了前i+1个数的最优解;否则的话,我们令bi=bi+1=mid,其中midai,ai+1的中位数,这样的话就找到了在前i1b不变的情况下的最优解(因为此时根据子问题最优性,j=1i1|ajbj|是最小的,而根据上面的问题的回答,j=ii+1|ajbj|也是最小的,两段同时取到了下界,于是为最优解)。然后考虑bi1,如果midbi1,那么就停止循环,否则的话,令bi1=bi=bi+1=Mid(其中Midai1,ai,ai+1的中位数)。一直重复上述操作直到一个下标q(不能再继续操作了,因为已经构造了的后面一段的中位数不低于前面一段了),现在b的前q个数是我们之前已经构造好了的数,同时也是使得j=1q|ajbj|最小的构造(原问题满足子问题最优性),bq后面的b的值都相同,为aq后面的a的中位数,显然此时也有j=q+1i+1|ajbj|最小,于是原问题就取到了下界

但是上述过程一个b一个b地考虑太慢了,考虑优化。实际上,当我们循环到i+1的时候,b都是一段一段的,如下

image

当我们加入ai+1的时候直接与最后一段b进行比较,如果ai+1<bi,那么根据上面的第二个问题,可以直接找出k2+1 ~ i+1的最优解,此时如果ak2+1 ~ ai+1的中位数不低于前一段b(即k1+1 ~ k2这一段),那么直接插入,否则根据问题二可以继续合并;直到某一时刻可以直接插入了就停止循环。此时一定是最优解,因为在插入位置的前面,有|aibi|取到最小值,在插入位置的后面,有|aibi|取到最小值,于是得到最优解

所以现在要解决的问题就是,如何快速得出两个数列合并之后的中位数。我们尝试用大根堆左偏树维护数列的前一半的数(如果数列有偶数个,那么堆顶为an2,否则的话为an+12)。一个自然的想法就是在合并两个线段的时候,直接合并两个左偏树,得到的新的左偏树的堆顶就是新数列的中位数。但实际上这个样子是错误的,一个反例就是一个数列的数都非常小,另一个数列的数都非常大,于是新数列的中位数来源于比较小的数的数列,但是这个值却没有在其左偏树中维护,于是我们得不到新的中位数的信息。这就说明了左偏树不能直接维护没有任何限制的合并线段的中位数。然而,这道题目却可以这么做。因为:每次我们加入一个数ai+1,假设ai+1小于最后一段b的值,那么就说明ai+1小于最后一段a的中位数,于是最后一段a加上ai+1的中位数一定会在ai+1和最后一段a前一半小的数中产生,就不会有上面的问题。归纳一下,假设我们已经正确合并了后面若干段aai+1得到了正确的中位数。对于接下来的一段a,设这一段a的下标为[l,r],那么就是说我们已经得到了[r+1,i+1]的中位数。由于[r+1,i]的中位数不低于[l,r]的中位数(这个尝试证明一下),加入了ai+1后,最多让[r+1,i]的中位数往前移动一位,分类讨论之后可以发现也不会存在上面所说的问题,可以直接用左偏树合并

时间复杂度是多少?每个线段会入队一次和出队一次。入队复杂度为O(1),出队复杂度为O(logn)

具体见打卡代码

posted @   最爱丁珰  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
历史上的今天:
2023-08-18 阶乘分解
点击右上角即可分享
微信分享提示