赛时\(8\)题(实际上是\(7\)题,有道题数据水而被\(fst\)了...)
E
这道题赛时做出来了,但看题解时看到了一个值得学习的重要结论。
结论:要将一个数组中的所有数变为相同的数\(x\),操作为加\(1\)或减\(1\),那么最小代价 \(<->\) \(x\)是中位数。
(若不知道这个结论做这个问题,可以将所有数当成若干在数轴上的点,固定点为\(x\),则显然\(x\)从负无穷到正无穷的过程中,代价是先减后增的山谷形,这样就可以用 三分 或者 二分斜率 来解决这个问题)。
证明:由括号内容可知最小代价和最终值的二元函数呈山谷形,故这个最终值一定是唯一的。假设这个值不是\(median\)。则:
- \(val > median\)时,数组中比\(val\)小的元素个数\(>\)比\(val\)大的元素个数(中位数的性质),前者通过加\(1\)达到\(val\),后者通过减\(1\)达到\(val\)。则将\(val\)减少\(1\),会使所有比\(val\)小的元素各减少一个代价,同时会使所有比\(val\)大的元素各增加一个代价。由于比\(val\)小的元素个数比\(val\)大的元素个数多,故总代价是减少的。因此最优解一定\(<val\)。
- \(val > median\)时,与\(1\)完全类似的证明,可以证得最优解一定\(>val\)。
得证。
code
F
双指针 + 前缀和技巧
要想用传统的“枚举右端点,二分左端点”的套路统计贡献,就必须要发现一些单调性。而可以发现,对于固定的右端点,左端点越偏左,子区间内包含的数的种类就会越多。而所规定的子区间恰好要满足包含种类数为\(2\)这一性质,因此可以采用这个套路。
可以用\(map\)的\(size\)函数快速获得要维护区间的数的种类数。
剩下的问题就是要判断子区间内两种数的数量是否相同。这里有个比较巧妙的\(trick\):将原序列的数转化为\(-1\),\(1\)的映射,这样只需要统计区间和为\(0\)的区间个数,即\(pre[l]==pre[r]\)的个数。那怎么建立这个映射呢?
可以发现,只需要顺序遍历序列,对于不同的数,\(1\)和\(-1\)交替赋值即可。由于能够保证子区间的数的种类数最多是\(2\),进而可以保证两种不同的数会被分别赋为\(1\)和\(-1\)。具体实现见代码。
维护区间内的前缀和数组信息时要特别注意\(pre[0]\)的维护。
code
I
code
K
\(ST\)表区间\(gcd\) + 二分 + 查询\(trick\)
与F题做法类似:区间\(gcd\)明显具有单调性,因此可以采用“枚举右端点,二分左端点”的套路。
而还要记住区间\(gcd\)的另一个重要性质:对于一个数组的所有子数组,不同\(gcd\)的数量最多为\(O(nlogA)\),\(A\)为数组中最大元素。
证明:对于某个特定的右端点\(r\),考虑所有左端点\(1<=l<=r\),所表示的\([l,r]\)区间:它们的\(gcd\)必然均为\(a[r]\)的约数。而\(a[r]\)的\(>1\)因子数量应为\(O(logA)\)级别,在左端点不断往左扩展时,\(gcd\)值减小,且是按因子个数不断减小的(相当于每次减小会除以某一个因子,这是证明该性质的关键!)因此,所有不同的\(gcd\)个数不会超过\(a[r]\)的因子的个数,即\(logA\);对于所有的右端点均是这样,因此所有子区间的不同\(gcd\)个数约为\(nlogA\)个。
这样,对于每一个右端点统计贡献时,就可以暴力寻找每一个左端点,当然也不是纯暴力,需要利用\(ST\)表+二分来找,由上述证明知寻找次数不超过\(logA\)次。
这样,问题就转化为:已知若干 右端点固定,左端点连续 的区间\(gcd\)为\(G\),找其中异或和也为\(G\)的区间数量。
不难想到前缀异或和来优化这件事情,即对于区间\([l,r]\):
\(prexor[r]\) ^ \(prexor[l - 1] = G\)
由异或交换律,将\(prexor[r]\)移到左边,得:
\(prexor[l - 1] = G\) ^ \(prexor[r]\)
问题转化为求左端点\(l - 1 <= x <= r - 1\)(注意有偏移,因为求前缀和的左端点是需要偏移的),\(prexor\)值为 \(G\) ^ \(prexor[r]\)的位置\(x\)的个数。这个可以用在\(map套vector\)上二分来做到静态查询。具体见代码。
code
L
code
M(不会证明)
被\(fst\)的那道题。赛时猜了个结论没想到直接就过了,赛后才发现自己对这道题还缺乏很多思考。
首先有个结论:最小值是一定要乘2的。这个证明到现在还是不太会,只能靠感性理解。
知道了这个结论,就不难想到每次从最小值位置开始,暴力地向次小值位置扩展区间并更新答案。
code