我 不 会 均 分 纸 牌
\(\color{red}\text{我 不 会 均 分 纸 牌}\)
\(\color{orange}\text{我 不 会 均 分 纸 牌}\)
\(\color{yellow}\text{我 不 会 均 分 纸 牌}\)
\(\color{green}\text{我 不 会 均 分 纸 牌}\)
\(\color{cyan}\text{我 不 会 均 分 纸 牌}\)
\(\color{blue}\text{我 不 会 均 分 纸 牌}\)
\(\color{purple}\text{我 不 会 均 分 纸 牌}\)
唐完了。
为啥不会啊?
线形均分纸牌问题
\(n\) 个人排成一排,每个人手上有 \(a_i\) 张牌,每次可以移动一张牌给左边或右边,问最少几次可以让所有人手上的牌数相等。
传统派。最后每个人手中的牌数是平均值 \(ave\)。令 \(b_i=a_i-ave\),\(s_i=\sum\limits_{j\le i}b_j\),答案就是 \(\sum\limits_{i=1}^n |s_i|\)。
正确性的话,第一个人要移走 \(|b_1|=|s_1|\) 个,他只能给第二个人。于是第二个人就要移走 \(|b_1+b_2|=|s_2|\) 个,依此类推。
环形均分纸牌问题
典例:P2512,也就是上面变成环形了。
维新派。并且有两种解法。
第一种
一个前提是肯定不会环形传递。不是很会证啊。
随便选一个人,假设他减去 \(ave\) 以后是正数。他有两个选择,一个是只向一边传递。那就肯定没有环形。还有一种是他向两边都传递,首先他自己不会再接受糖果了,其次过程中肯定会有一个人正好满足了,那他就不会接着往下传递了,而他接受了上一个人的糖果,所以他不是最初的那个人,那就也没有环形。是负数同理。似乎也不是很严谨啊
然后,你可以枚举那个断点。假设是 \(k\),那考虑传统派做法,前缀和变成了 \(s_{k+1}-s_k,s_{k+2}-s_k,\cdots,s_n-s_k,s_1+s_n-s_k,\cdots,s_k+s_n-s_k\)。注意到 \(s_n=0\),那也就是所有都减去了 \(s_k\)。答案变为 \(\sum\limits_{i=1}^n |s_i-s_k|\),你会发现取中位数是最佳的。
第二种
这个是推数学式子。
设 \(X_i\) 表示 \(i\) 给了 \(i+1\) 多少(特别地,\(X_n\) 表示 \(n\) 给了 \(1\) 多少)。那我们有:
然后先不管第一个,移项,变为
那么就设 \(C_i=\sum\limits_{j=2}^i ave-a_j\),则有 \(X_i=X_1-C_i\)。注意这里 \(i\ge 2\)。\(C_1=0\)。
所以答案就是 \(\min\sum |X_i|=min\sum|X_1-C_i|\)。于是找到中位数就行了。
然后你发现有一道题。这引起了你的思考:如果每次可以移动任意个,上述两个问题要怎么做呢?
线形均分纸牌问题'
还是减去 \(ave\),做前缀和,发现答案是非零的个数。显然。
环形均分纸牌问题'
首先还是不能环形传递,证明和上面类似。之后我们尝试枚举断点,但是发现这并不好计算,最快只能做到 \(n^2\)。
那么考虑我们的断点从 \(i\) 变为 \(i+1\) 时是怎样的。相当于我们把 \(i\) 扔到了序列最末尾并动态维护前缀和,查询全局等于 \(0\) 的个数。肯定可以开一个长度为 \(2n\) 的线段树。如果想做到更优,考虑用 unordered_map
维护一个桶。我们把 \(i\) 扔到最后面时,根据前面的讨论,相当于全局减去 \(s_i\)。我们记录每次的修改量之和 \(\Delta\),查询时不再是查有多少个 \(0\),而是查有多少个 \(-\Delta\)。时间复杂度是线性的。
写这个有啥用?
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步