CF2121G G. Gangsta题解

CF2121G G. Gangsta题解
Shadow DrunkG. Gangsta
题目翻译
题目给我们一个长度为n的二进制字符串s,我们要计算s所有子数组中函数f值,f(s)的值是二进制字符串中最多的相同字符的个数。
思路
我们考虑如何暴力的做这道题目,定义状态dp[i][j]表示以i为结尾1的个数减去0的个数等于j的子段的数量,ans[i]表示以i为结尾的所有子段做的贡献。我们考虑拓展到i + 1,首先ans[i]的贡献在i + 1时一定会产生,然后考虑新增的贡献,假设此时s[i] == ‘1’,那么原先dp[i][j]中j >= 0的都会新增贡献,s[i] == ‘0’时也是同理。但是这个dp暴力维护的时间复杂度是O(n ^ 2)的。
考虑如何优雅的维护,我们发现遇到1时,实际上原先1的个数减去0的个数为-1的子段变成了为0的子段,我们可以标记一个offset来表示实际上此时为子段中1的个数减去0的个数的位置,我们注意到每次offset只会变动1个位置,我们可以O(1)的维护出大于它的(也就是上面dp中j >= 0)的个数,可以快速算出此时新增的贡献,注意别忘了还要加上ans[i]的贡献(也就是右端点位于上一个位置的贡献)。遇到0时也是同理。实际上这个维护思路我觉得有点类似长链刨分优化dp。时间复杂度O(n)
举个例子,对于10011,初始offset为0,考虑第一个元素1,此时count数组中count[0] += 1,offset -= 1,此时offset为-1,所有count[j] (j > -1)都会做贡献,此时新增贡献1。考虑第二个元素0,此时count[-1] += 1, offset += 1,,此时offset为0,所有count[j] (j < 0)都会做新增贡献,此时新增贡献1,注意此时右端点在位置2的总贡献还要加上右端点为1位置的1,实际总贡献为2。
代码
1 | void solve(){ |










