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

    传送门

    算法 : 瞎搞......

    这种题是真的恶心....

    以下为一堆的结论和证明...

    (自己口胡的,比较细,实在不想看也可以直接看结论)



    首先如果要让每个人最终的糖果一样多

    1.那么肯定最终每个人的糖果数量为 每个人糖果数量的平均数..(显然...)

    还有一个显然的结论:

    2.如果 a 把糖分给别人,那么 a 就不应该再收到糖

    不然就不可能是最优方法(a 还不如少分一点糖给别人,这样糖果的交换数量还更小)

    当然反过来也一样。

    3.如果 a 的糖果数量大于平均数,那么 a 一定要分糖给别人(由 1. 可得)

    并且 a 也不会再接受其他人的糖(由 2. 可得)

    反过来也是一样: 

    如果 a 的糖果数量小于平均数,那么 a 一定要从别人接受糖,并且 a 也不会分给其他人糖。

    所以有一种比较好的方法:对于每个人只要把多出来的糖传给下一个就好了

    如果是缺少糖就把需求传给别人,让别人把需求传下去,设多的是正数,那么需求就为负数,如果负数被抵消了就说明有正数把需求"填满"了,相当于有多出来的糖返回给缺少的人了....(这里可能需要一点网络流反向边的思想...)。

    (但是网络流是不可能的,数据太大,直接凉了)

    设第 i 个人 原本的糖果数量为 a[ i ],第 i 个人原本的糖果数量减去平均数量 为 b[ i ],(b[ i ] 可能为负数,表示 i 需要 abs( b[ i ] ) 个糖)

    那么最终就是要经过转移让所有的 b 都为 0 。

    先考虑第一个人 k 怎么分(因为是环形,所以每个人都可以是第一个人)

    由 3. 可得,要分 b[ k ] 个糖

    那要怎么分(可以左边给几个,右边给几个,也可以只给一边)

    方案太多不好搞

    但是要注意:4.分给两边其实相当于一边的某一个点把多出来的全部分给另一边....

    怎么描述呢:

      就是说 k 分给左边几个糖,分着分着,分到点 j 分完了,剩下的分给右边,最后分到 j 肯定也刚好分平均(由 1. 易证得)

      那就相当于点 j 把要分的的糖全部分给右边

    怎么抽象地证明呢:

      自己动手,丰衣足食...
      也不难,k 分给左边几个糖,分到 j 没了,说明 j 是有需求的

      相当于 j 带着需求跑到 k ,这时 j 的需求也刚好没了,然后 k 少了几个糖,把剩下的糖往另一边分

      也就相当于 j 带着全部需求跑了一圈,跑完就分完了

    所以由4.可知

    我们只需要判断每个点把(需求 or 盈余)只往一边分需要多少糖

    对于第一个点 k 要分b[ k ] 个,则对于 k+1 要分 b[k+1]+b[ k ],......,对于 k+m 则要分 b[ k,k+1,...,k+m] 个

    那要怎么快速计算这种东西呢

    前缀和...

    先预处理出 1 到 n 的前缀和,为sum

    对于前面的式子,可以化为 abs(sum[ k ]-sum[ k-1 ]),abs(sum[ k+1 ]-sum[ k-1 ]),...,abs(sum[ k+m ]-sum[ k-1 ])

    那要怎么选取 k-1 才能使之和最小呢

    稍微转换一下:

    把每个sum都放到数轴上,则前面的式子就是某个点 k-1 到所有其他点的距离之和

    怎么找最小的和那就是“货仓选址”问题

    好吧讲了这么多终于可以发代码了......

    代码难度为0.....

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    inline int read()
    {
        int x=0,f=1;
        char ch=getchar();
        while(ch<'0'||ch>'9')
        {
            if(ch=='-') f=-1;
            ch=getchar();
        }
        while(ch>='0'&&ch<='9')
        {
            x=(x<<1)+(x<<3)+(ch^48);
            ch=getchar();
        }
        return x*f;
    }
    int n;
    int a[1000007],b[1000007];
    long long ans,tot;
    long long sum[1000007];
    int main()
    {
        n=read();
        for(int i=1;i<=n;i++)
            a[i]=read(),tot+=a[i];
        tot/=n;
    
        for(int i=1;i<=n;i++)
            b[i]=a[i]-tot;
    
        for(int i=1;i<=n;i++)
            sum[i]=sum[i-1]+b[i];
    
        sort(sum+1,sum+n+1);
        tot=sum[(n+1)/2];
    
        for(int i=1;i<=n;i++)
            ans+=abs(tot-sum[i]);
    
        cout<<ans;
        return 0;
    }
  • 相关阅读:
    在IE和Firfox获取keycode
    using global variable in android extends application
    using Broadcast Receivers to listen outgoing call in android note
    help me!virtual keyboard issue
    using iscroll.js and iscroll jquery plugin in android webview to scroll div and ajax load data.
    javascript:jquery.history.js使用方法
    【CSS核心概念】弹性盒子布局
    【Canvas学习笔记】基础篇(二)
    【JS核心概念】数据类型以及判断方法
    【问题记录】ElementUI上传组件使用beforeupload钩子校验失败时的问题处理
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/9537513.html
Copyright © 2011-2022 走看看