zoukankan      html  css  js  c++  java
  • Luogu P2512 糖果传递

    题目大意

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

    题解

      环形实际上就是(n)种链形。(证明见下)
      我们先考虑第(1)(n)个小朋友形成链形的情况。
      我们设

    [s_i = sum_{j = 1}^{i} (a_i - overline{a}) ]

      则此时对于每个(i),第(i)个小朋友传递的代价是(s_i)
      此时我们再回到环形,如果我们在第(k)个小朋友处拆成链形,则当(i geq k)时,第(i)个小朋友传递的代价为

    [| s_i - s_k | ]

      当(i < k)时,第(i)个小朋友传递的代价为

    [| s_i + s_n - s_k | ]

      根据我们前面的定义,可以得到,(s_n)恒为(0)
      所以当(i < k)时,第(i)个小朋友传递的代价也为

    [| s_i - s_k | ]

      所以总代价为

    [sum_{i = 1}^{n} | s_i - s_k | ]

      根据贪心策略,显然这里的(s_k)应为({ s_i })中的中位数,排序处理一下即可。

    #include <iostream>
    #include <cstdio>
    #include <cctype>
    #include <algorithm>
    
    #define MAX_N (1000000 + 5)
    #define SIZE (1 << 21)
    
    #define abs(x) ((x) >= 0 ? (x) : -(x))
    #define Getchar() (p1 == p2 && (p2 = (p1 = fr) + fread(fr, 1, SIZE, stdin), p1 == p2) ? EOF : *p1++)
    
    using namespace std;
    
    char fr[SIZE], * p1 = fr, * p2 = fr;
    
    void Read(int & num)
    {
        num = 0;
        char ch = Getchar();
        while (!isdigit(ch)) ch = Getchar();
        while (isdigit(ch)) num = num * 10 + ch - '0', ch = Getchar();
        return;
    }
    
    int n;
    int a[MAX_N];
    int s[MAX_N];
    long long ans;
    
    int main()
    {
        Read(n);
        long long tmp = 0;
        for (int i = 1; i <= n; ++i)
        {
        	Read(a[i]);
        	tmp += a[i];
        }
        tmp /= n;
        for (int i = 1; i <= n; ++i)
        {
        	s[i] = s[i - 1] + a[i] - tmp;
        }
        sort(s + 1, s + n + 1);
        tmp = s[n >> 1];
        for (int i = 1; i <= n; ++i)
        {
        	ans += abs(s[i] - tmp);
        }
        printf("%lld", ans);
        return 0;
    }
    

    终于把坑填上了。。。
    证明
    (d_i)(i)(i+1) 传递的数量,显然 (a_i + d_{i-1} - d_{i} = overline{a})(d_{i-1} = overline{a} - a_i + d_{i})
    (d_i = sumlimits_{j=i+1}^{n}(overline{a} - a_j) + d_n)
    (s_i = sumlimits_{j=i}^{n}(overline{a} - a_j))
    (d_i = s_{i+1} + d_n)
    (ans = sumlimits_{i=1}^{n} |d_i| = sumlimits_{i=1}^{n}left|s_{i+1} + d_n ight|)
    (d_n = -s_{ ext{medium}})(ans) 最小。(这个容易证明吧)
    所以一定存在一个 (d_i = 0) ,此时就在 (i)(i+1) 中间拆开变成链即可。
    话说证出来之后顺便就把题做完了啊。。。。

  • 相关阅读:
    mybatis SQL 根据in条件语句排序
    Redis面试总结
    数据库优化之分库分表
    jdk1.6 Synchronized 优化总结
    CounDownLatch、CyclicBarrier、Semaphore
    java锁总结
    Redis 与 MySQL 双写一致性如何保证
    dubbo总结
    一、全国大学生电子设计竞赛测控(无人机)方向___基础篇
    编解码KL变换详解和哥伦布k阶编解码
  • 原文地址:https://www.cnblogs.com/kcn999/p/11360577.html
Copyright © 2011-2022 走看看