1. 程式人生 > 其它 >P2512 [HAOI2008]糖果傳遞

P2512 [HAOI2008]糖果傳遞

技術標籤:題解c++

P2512糖果傳遞

題目描述

n n n 個小朋友坐成一圈,每人有 a i a_i ai 個糖果。每人只能給左右兩人傳遞糖果。每人每次傳遞一個糖果代價為 1 1 1
連結

思路

在想到解決方法之前,我們首先可以得到一些顯而易見的結論:

  1. 最終時每個人手中的糖果數都為總糖果數的平均值,記為 a v av av
  2. 記小朋友 i i i i + 1 i+1 i+1 的糖果數為 x i x_i xi(對於 n n n,是給 1 1 1 的),對於每一個小朋友的糖果數變化量,可以列出: a i − a v = x i − x i − 1 a_i-av=x_i-x_{i-1}
    aiav=xixi1
    (當然 1 1 1 又是特殊的)
  3. 存在方案可以保證,在傳遞的過程中不會出現有小朋友出現負數糖果。
  4. 最終答案等於 ∑ i = 1 n ∣ x i ∣ \sum_{i=1}^n |x_i| i=1nxi

可以想到將所有的 2 中的方程聯立,等價於 n − 1 n-1 n1 個(有一個能由其他方程推出)個方程: x i = x i − 1 + a i − a v x_i=x_{i-1}+a_i-av xi=xi1+aiav,很明顯這是一個遞推公式,得到通項公式:
x i = x 1 − ( i − 1 ) a v + ∑ i = 2 n a i x_i=x_1-(i-1)av+\sum_{i=2}^na_i

xi=x1(i1)av+i=2nai
於是我就有了驚人的發現: a n s = ∑ ∣ x 1 − ⋯ ∣ ans=\sum|x_1-\cdots| ans=x1
這是一道初一數學題!
那就可以把後面減去的那一堆變成一個數列求出來(當然是遞推求),初中數學告訴我們,當 x 1 x_1 x1 等於數列的中位數時 a n s ans ans 最小。
居然就這麼出來了?

Code

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define ll long long using namespace std; int n,a[1000001],num[1000001]; ll ave; int main() { cin>>n; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); ave+=a[i]; } ave/=n; for(int i=2;i<=n;i++)num[i]=num[i-1]+a[i]-ave; sort(num+1,num+n+1); ll ans=0; int mid=num[n/2+1]; for(int i=1;i<=n;i++)ans+=abs(num[i]-mid); cout<<ans; return 0; }

強行拓展

上面思路中有一個及其重要的思想:在任意區域內某種守恆量總量的改變,等於從邊界進入或離去的數量。在拓展一下,對於不守恆的量的改變,等於從邊界進入或離去的數量再加上內部產生的量。
看起來是廢話,但這是一句有用的廢話。
所以,你想到連續性方程了嗎?

用這句看似廢話的話,就能得到基爾霍夫電流定律,流體基本連續性方程,熱流方程……
總之,大道至簡,最簡單的思想往往有最強大的威力。

行了別拓展了,週四開始寫,現在都週五了