因为昨天模拟赛考了这个题,并且爆 0 了,所以应当为此题写一篇题解。
题意
有 个小朋友坐成一圈,每人有 个糖果。每人只能给左右两人传递糖果。每人每次传递一个糖果代价为 。求使所有人获得均等糖果的最小代价。。
点击查看题解
题解
思路 1
环形均分纸牌。P1031 [NOIP2002 提高组] 均分纸牌。
一般的均分纸牌问题就相当于在第 个人与第 个人之间把环断开,此时这 个人站成一行,第 人持有的纸牌数、前缀和分别是 ,。设 为答案。设 为平均数。不在上面加横杠了,因为懒。
如果在第 个人之后把环断开站成一行,则:
- ,第 个人持有的数目的前缀和是 。
- ,第 个人持有的数目的前缀和是 。
我们考虑整体和隔离的思想。回到一般的均分纸牌问题,将前 个看做一个整体,显然前 个内部的均分是不会改变其整体结构的,因而对于该体系来说,想要达到平均结构,就必须与下一个体系交换足够的纸牌,而交换数量就是 ,然后就可以推出一个结论:
也就是将每次体系更新的贡献加起来。
若设 , 是 的前缀和,则:
结合上面的式子,得:
这就是均分纸牌问题的通用公式。
对于环形问题,首先考虑切开。因为 ,所以 。所以 前缀和的变化仅仅是减去 。所以,所需最小花费为:
那么,现在是要找到一个 ,使得上面的式子的值最小。显然, 就是 数列的中位数(由于思路 2 也要用到这个结论,所以证明在思路 2)。
所以就做完了。找第 大的数存在 算法,所以时间复杂度为 。
思路 2
显然,要使每个人获得均等糖果,每个人的糖果数应为 。设第 个人需向右传 个糖果,对于第 个人易得式子:
变形得:
,展开递推式,得:
设 ,则得:
题目要求最小化:
即:
那么,至少预处理所有 是可以 的。现在是要找到一个 ,使得上面的式子的值最小。显然, 就是 数列的中位数。
证明:把每个 对应到数轴上。设 。对 和 两两配对。那么对于每个数对 ,两者距离最小值一定是 (两点之间线段最短)。则在数轴上, 可以选择在 , 中间。这样,考虑每个数对,最后易得 就是 数列的中位数。由于 ,易得其正确性。
所以就做完了。找第 大的数存在 算法,所以时间复杂度为 。
代码
#include<bits/stdc++.h>
using namespace std;
namespace _yz
{
typedef long long ll;
const ll N=1000000+10;
ll n,a[N],s[N],c[N],x,mid,ans=0;
void solve1()
{
for(ll i=1;i<=n;i++) a[i]-=x;
for(ll i=1;i<=n;i++) s[i]=s[i-1]+a[i];
nth_element(s+1,s+mid,s+1+n);
//nth_element(i,j,k,l)
//i,j,k 都是迭代器。在 [i,k) 区间内进行重排,
//使得比 j 大的和比 j 小的以 j 为分界分开。
//此时第 j 大的元素刚好是第 j 个元素。
//l 是自定义函数,判断大小。
for(ll i=1;i<=n;i++)
ans+=abs(s[i]-s[mid]);
}
void solve2()
{
for(ll i=1;i<=n;i++) s[i]=s[i-1]+a[i];
for(ll i=1;i<=n;i++) c[i]=s[1]-s[i]+x*(i-1);
nth_element(c+1,c+mid,c+1+n);
for(ll i=1;i<=n;i++)
ans+=abs(c[i]-c[mid]);
}
void yzmain()
{
scanf("%lld",&n);
for(ll i=1;i<=n;i++)
{
scanf("%lld",a+i);
x+=a[i];
}
x/=n;mid=(1+n)/2;
solve2();
printf("%lld\n",ans);
return;
}
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
_yz::yzmain();
return 0;
}
本文作者:Yurchiu
本文链接:https://yz-hs.github.io/1a06ceae98f4/
版权声明:本博客中所有原创文章除特别声明外,均允许规范转载,转载请注明出处。所有非原创文章,按照原作者要求转载。