CF div2 997(A~E)

赛时3题,比上一把还要手速场。虽然手速慢了一些,但好在没掉分,并重新回到蓝名了。

A

很套路的题目。第一次移动没有用,把其余方向的移动距离求和加边长,取2倍就是答案。

code

B

相当于告诉了你一个排列中任意一对i<j的前后位置关系,比如:

  • g[i][j]=0,代表ij前面
  • g[i][j]=1,代表ij后面

取整个nn矩阵的上三角矩阵(保证i<j)。对于每个i,枚举第i行中1的个数(设其为cnt),即代表在排列中i后面有cnt>i的数。按照从1n顺序确定每个元素在排列中的位置。

确定每个数位置的具体做法:倒序枚举排列的每个位置,最终后面 有cnt个空位置 的空位置即为 第i个元素的位置(因为是顺序枚举,保证了插入过的数都是<i的数,所以空位置上的数都将成为>i的数。而cnt代表i的后面>i的元素数量,即为空位置的数量)

code

C

见到过的最sb的div2 C,不过也被这道诈骗题硬控了40min

想一下,题目并没要求最长回文串的长度,故可以猜想:这个最长回文串长度越短,数量可能就会越多。由于1n的排列就已经有n个最长回文串了(最长为1,每个数作为一个回文串),若要保证最后构造的序列的最长回文串数量>n,显然这个最长回文串的长度不能是1,也不能是2(这种情况必须要相邻的一对相同数字才行,显然数量不够)。故考虑构造最长回文串长度为3的序列即可。构造方式有很多,随便试一个就可以了。注意某些构造方式的n=6情况需要特判。

code

D

中位数&子区间相关trick

回顾一下这个trick:对于一个序列,设定一个中位数x,并对这个序列做一一映射:

  • >=x的数映射为1
  • <x的数映射为1

设映射后的数组为b,并求出其前缀和数组preb,这样就能快速判断出该序列的任意子区间的中位数和x的大小关系。考虑区间[l,r],设该子区间的中位数为mid

  • preb[r]preb[l1]>=0,则一定有mid>=x
  • preb[r]preb[l1]<0,则一定有mid<x

证明略

这样就可以解决中位数,子数组结合起来的相关问题。比如要求出中位数>=x的子数组数量,就可以处理出preb,问题转化为求满足preb[r]preb[l1]>=0[l,r]的个数。

同时,中位数还经常与二分结合,因为中位数一般会和单调性相关。比如这道题:CF947C

应用:确定某个子数组的中位数:直接二分中位数(O(logn)),根据这个中位数处理出对应的preb数组(O(n)),看preb[r]preb[l1]>=0即可,最后用二段性确定出中位数。

回到本题。题意就是要对一个数组统计出 偶数长子数组排序后最中间的两个数 相同的子数组的个数,这里可以将这两个数理解为两个中位数。

由于ai<=10,故可以考虑枚举中位数x110中的某个数,然后处理出对应的 bpreb 数组。若对应某个[l,r]有:preb[r]preb[l1]==0,则这个区间一定是不好的数组(一半1一半1,最中间的两个数一定是左侧的映射为1,右侧的映射为1,这两个数一定不同。反之不成立,因为映射的数相同不代表原来的数相同)。由此可见,统计不好数组的数量会比统计好数组数量容易。因此转换思路,求原数组中不好数组的总数量。

按照上述想法,直接枚举每个中位数,并看满足 preb[r]preb[l1]==0[l,r]数量即可。但这样还是不对,问题就在于会重复统计某一个不好数组的贡献,因为对于不同的中位数,映射的序列可能会是相同的,而每个不好数组的贡献只需要被统计一次。

那怎么避免重复统计贡献呢?由于每个子区间的中位数是唯一的,而在枚举过程中也枚举到了所有可能的中位数。故可以这样想:在枚举某个中位数时,若统计到了某个子数组的preb区间和为0,那么只有当这个数组中出现了这个中位数时才统计贡献,否则不统计。这样就只需要额外判断一下该子区间内是否存在正在枚举的中位数即可,保证了只有在枚举到该子数组中位数时才会被统计贡献,进而保证了唯一性。

判断某个数是否在子区间内出现:由于值域很小,故对每个数预处理一个高维前缀和即可。

复杂度:O(10n)

code

E

卡特兰数 + 递归

首先对于题目中“任意一对线段 互相包含 或者 互不相交” 的性质,可以想到卡特兰数中的括号序列(任意一对括号也满足互相包含或互不相交),因此可以想到卡特兰数。

现在考虑某一条线段的拆分:可以拆成两个或多个连续的线段。但显然直接拆为两条线段的情况才能对应总线段数最多的情况,因为>2条连续的线段可以被两条更长的线段所囊括。因此拆任意一条线段时,一定是先拆为两条线段,再继续对这两条线段做拆分,这样才能保证总线段数最多。

而每一条线段可以想象为一个结点,在该线段内拆分出的子线段对应该结点的子节点,这样形成的结构就是一个树。又由上段可知,形成的树一定是一棵二叉树,并且每个结点要么无子节点,要么有两个子节点(即广义上的满二叉树),而叶节点对应的是每一个l<=i<=r[i,i],故对于长为len的线段,必含有len个子节点。由卡特兰数的性质:n+1个叶节点的广义满二叉树的形态数为Catalan(n),可以直接计算出长为len的线段经拆分后的方案数。

m=0,则方案数即为Catalan(n1),因为线段[1,n]内部没有限制,可以随意划分二叉树的结构。但当m!=0,即有固定的线段时,有些二叉树形态就不满足要求了。

可以将每一个固定的线段想象为一个特殊的结点,该结点特殊在——其内部还可以继续做划分。这样就可以递归地处理该问题了。具体细节见代码。

复杂度:O(n)

code

posted @   jxs123  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示
主题色彩