清华月赛 Yazid的新生舞会题解

  • Post author:
  • Post category:其他


算法一(针对








n





300











的测试点)

考虑枚举所有区间,然后求其众数及出现次数,并判断是否超过区间总长的一半,统计答案即可。时间复杂度








O




(






n






3








)












算法二(针对








n





2


,


000











的测试点)

考虑先枚举区间的左端点








l











,再从左到右枚举右端点









r












并用数组维护每个数的出现次数,同时使用变量维护当前众数、众数的出现次数。不难发现,当右端点向右移动时,这些信息都是非常方便维护的。于是我们便可以在








O




(






n






2








)













的时间复杂度内统计所有答案。

算法三(针对








t


y




p


e


=


1











的测试点)

对于








01











序列,我们不难发现,众数出现次数严格大于子区间长度当且仅当子区间内








0


,


1











出现次数不同(那么那个出现次数较多的就是众数)。于是我们将原序列中的








0











视作












1












,并对该序列求前缀和








S













,则子区间









[


l


,


r


]















为“新生舞会的”,当且仅当











S










l





1









=





S








r
















,因此对








S













进行排序并从小到大枚举便可求出答案。

算法四(针对









t


y




p


e


=


2












的测试点)

对于所有数的出现次数都较小(不超过








15











)的情况,不难发现所有“新生舞会的区间”的长度也会较小(不超过








2


×


15





1


=


29











)。于是使用算法二,枚举所有长度少于








30











的区间并统计答案即可。

算法五(针对








t


y




p


e


=


3











的测试点)

对于











A






i










7











的测试点,我们不妨枚举所有值作为众数的情况。考虑统计所有众数为








k











的“新生舞会的”区间,将所有等于









k












的位置取为








1











,不等于









k












的位置取为











1











,得到新序列








B











并求前缀和得到序列









S














,则区间








[


l


,


r


]











需要被统计,当且仅当











S








r













S










l





1












1











。从左到右枚举右端点,并用线段树维护当前右端点左边每种前缀和出现的次数即可。时间复杂度








O



(


8


n


l


o


g




n


)











算法六(针对所有测试点)

考虑改进算法五。我们考虑取出所有








B











中的极长












1












子区间,观察这些区间中的所有点作为右端点对答案的贡献。不难发现极长











1











子区间








[


l


,


r


]











中的所有点作为右端点对答案的贡献为




















r





l


+


1










i


=


1
























S










l





1












i





1










j


=















c


n


t


[


j


]











,其中








c


n


t


[


j


]











表示在区间








[


0


,


l





1


]











之间前缀和为








j











的端点数目;在统计这段区间的答案后,我们还需要对区间









[





S










i





1












(


r





l


+


1


)


,





S










i





1












1


]












中的所有








c


n


t











均进行








+


1











操作。显然地,我们使用一个维护











B






i




























C








i







=


i


×





B






i
















的线段树就可以支持这些查询、修改操作。于是我们使用这棵线段树维护相关信息,并从左到右枚举右端点,统计答案即可。

不难发现,极长











1











子区间的数目与序列中








k











的数目同阶,因此,对于统计众数为









k












的“新生舞会的”区间的时间开销,不难发现我们通过上述优化将时间开销缩短到了








O




(





















A











k





























l


o


g




n



)












于是,总时间复杂度即为








O




(















k



























A











k





























l


o


g




n



)













,即








O



(


n


l


o


g




n


)





















n





100


,


000











测试点的说明

对于常数较大的与标准算法时间复杂度相同的算法,以及一些时间复杂度略大于标程的算法,可能存在无法通过所有测试点的情况。这类算法可以通过这类测试点。