atcoder_ABC290E
E - Make it Palindrome
题目大意
对于数列 ,记 为修改其为回文串的最少次数。现在给定数列 ,求出 的所有连续子序列进行 操作的和。
思路
注意到数据范围 ,肯定是过不了的,我们要想更优的算法。但是复杂度更低的算法不是一下子就能想到的,所以我们可以考虑先想出 的做法,再对其进行优化。
解法
既然要在 内解决问题,意味着对于任意一个连续子列,我们只能枚举它的头和尾,而我们就要通过所有子列的头和尾来得出答案。我们不妨来看一个例子:
1 | 5 2 1 2 2 |
对于如上的数列,如果我们枚举到了第一和第四个元素,即 的话,显然是需要进行一次操作的,但是这个连续子列中间的 我们怎么处理呢?事实上,由于我们能够枚举到所有的数对,所以在之后的循环中 是肯定能被枚举到的,我们只需要在那个时候让操作次数再加一即可。至此,我们就得到了 做法的思路:枚举所有点对,如果不相等,就判断最多能向外延展多少,让操作次数 = 1 + 延展数量,最后就能得到总操作次数。
1 | int num[maxn]; //存放题目给出的数列 |
解法
现在让我们考虑优化。不难发现,对于任意一个点对,它延展的范围是有限的,同时点对的数量也是有限的,所以我们最多的操作次数是有限的。知道这个对我们来说有什么用呢?这意味着对于一个序列,我们可以用它的总可操作次数减去不用操作的次数,剩下的就是需要操作的次数。那为什么这样做更优呢?还是对于上述序列,我们考虑五号位的 ,由于五号位的 不能再向外延展,所以所有 的包含五号位的点对就是它的贡献。这样之后,我们就不用再考虑五号位的 了。我们可以用同样的思路来处理二号位和四号位的 。
如果我们这样做的话,在枚举所有元素的时候,当我们第一次枚举到 ,就处理完了所有 的贡献,之后就不需要枚举 了;而对于 的处理,每一个位置的 我们只利用了一次。所以从整体上来说,相当于我们仅遍历了序列所有元素一次,时间复杂度为
1 | int a[maxn]; //原数列 |
评论