zoukankan      html  css  js  c++  java
  • [HAOI2008]糖果传递

    [HAOI2008]糖果传递

    Description

    (n)个小朋友坐成一圈,每人有(a_i)个糖果。每人只能给左右两人传递糖果。每人每次传递一个糖果代价为(1)

    Input

    第一行一个正整数(n<=1000000),表示小朋友的个数.

    接下来(n)行,每行一个整数(a_i),表示第i个小朋友得到的糖果的颗数.

    Output

    求使所有人获得均等糖果的最小代价。

    Sample Input

    4
    1
    2
    5
    4

    Sample Output

    4

    环形均分纸牌

    本来以为均分纸牌很简单,才发现很多东西理解不到位,这里直接转载大佬的题解啦。

    原题解戳这里

    首先这道题是一个模型,我们称之为环形均分纸牌。显然这个模型是出自luogu1031那道均分纸牌,没有过的可以先过一下。

    先来看不是环形的情况。

    假设总和为 (T) ,有 (M) 张纸牌。设 (ave = dfrac{T}{M}) 。对于第(i)个人来说,如果 (C[i] < ave) 他拿的应该是 (C[i] + ave) 张,后一个拿 (C[i + 1] - ave + C[i]) ,否则,他拿 (C[i] - ave) 张,而后一个拿 (C[i + 1] + C[i] - ave) 张。

    但是这样并不方便维护,我们考虑整体和隔离的思想。将前(i)个看做一个整体,显然前(i)个内部的均分是不会改变其整体结构的,因而对于该体系来说,想要达到平均数结构,就必须与下一个体系交换足够的纸牌,而交换数量就是 (|G[i] - i cdot ave|) ,其中 (G[i]) 是前缀和。然后就可以推出一个结论: (d = sum ^M _{i = 1} |i cdot ave - G[i]|),也就是将每次体系更新的贡献加起来。

    如果让每个人的数量都减去 (ave) ,结果就可以经过简单的数学推导进一步化简: (d = sum ^M _{i = 1} |S[i]|) ,其中 (S[i]) 是新数组的前缀和。这就是均分纸牌问题的通用公式。

    现在考虑一种变形:如果这里的纸牌是环形的呢?

    对于环形问题,首先考虑切开。假定我们切开的东西是 (A[k + 1], A[k + 2], ..., A[M], A[1], ..., A[k]) ,那么其前缀和也会有所变化,即 (S[k + 1] - S[k], S[k + 2] - S[k], ..., S[M] - S[k], .S[1] + S[M] - S[k], ..., S[M])

    由于均分之后, (S[M] = 0)恒成立,所以前缀和的变化仅仅是减去 (S[k]) 。那么,我们要求的就是哪个取值上最短,换言之,求什么时候 (sum^M_{i = 1} |S[i] - S[k]|) 取到最小。

    因而,这里我们要求的东西是 (sum^M_{i = 1} |S[i] - S[k]|) 的最小值。答案是在中位数处取到,原因各位可以想象将 (S[i]) 投影到一个坐标平面内。然后我们用一条线去扫,点到线的距离之和就是上面的式子的最小值。从中位数的位置变化到靠下的位置或是靠上的位置,都会使某一部分点的距离增大。所以这里转化为求中位数,也就是求第 (dfrac{n + 1}{2}) 大元素,由于 (N leq 10^6) 所以不建议排序(虽然也能用)。我们可以用(STL)的nth_elements()函数(O(n))求出(k)大。

    讲的已经很清楚了,直接看代码吧

    #include<bits/stdc++.h>
    #define lll long long
    using namespace std;
    lll read()
    {
    	lll x=0,w=1;char ch=getchar();
    	while(ch>'9'||ch<'0') {if(ch=='-')w=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return x*w;
    }
    lll n,ave,qwe,ans;
    lll a[1000010],sum[1000010];
    int main()
    {
    	n=read();
    	for(lll i=1;i<=n;i++)a[i]=read(),qwe+=a[i];
    	lll ave=qwe/n;
    	for(int i=1;i<=n;i++) a[i]-=ave,sum[i]=sum[i-1]+a[i];
    	sort(sum+1,sum+1+n);int cut=sum[(n+1)/2];
    	for(int i=1;i<=n;i++) ans+=abs(sum[i]-cut);
    	cout<<ans<<endl;
    }
    
  • 相关阅读:
    一则线上MySql连接异常的排查过程
    有一种娱乐叫看别人编程
    程序员DNS知识指南
    中国式开源
    RSS与公众号
    论国人的素质和一个公司的商业道德
    《阿里游戏高可用架构设计实践》阅读笔记
    《淘宝数据魔方技术架构解析》阅读笔记
    软件体系架构_系统质量属性场景描述_结合《淘宝网》实例
    《余额宝技术架构及演进》阅读笔记
  • 原文地址:https://www.cnblogs.com/lsgjcya/p/9438185.html
Copyright © 2011-2022 走看看