P2512 [HAOI2008]糖果傳遞
阿新 • • 發佈:2021-02-05
P2512糖果傳遞
題目描述
有
n
n
n 個小朋友坐成一圈,每人有
a
i
a_i
ai 個糖果。每人只能給左右兩人傳遞糖果。每人每次傳遞一個糖果代價為
1
1
1。
連結
思路
在想到解決方法之前,我們首先可以得到一些顯而易見的結論:
- 最終時每個人手中的糖果數都為總糖果數的平均值,記為 a v av av。
- 記小朋友
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}
- 存在方案可以保證,在傳遞的過程中不會出現有小朋友出現負數糖果。
- 最終答案等於 ∑ i = 1 n ∣ x i ∣ \sum_{i=1}^n |x_i| ∑i=1n∣xi∣
可以想到將所有的 2 中的方程聯立,等價於
n
−
1
n-1
n−1 個(有一個能由其他方程推出)個方程:
x
i
=
x
i
−
1
+
a
i
−
a
v
x_i=x_{i-1}+a_i-av
xi=xi−1+ai−av,很明顯這是一個遞推公式,得到通項公式:
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−(i−1)av+i=2∑nai
於是我就有了驚人的發現:
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;
}
強行拓展
上面思路中有一個及其重要的思想:在任意區域內某種守恆量總量的改變,等於從邊界進入或離去的數量。在拓展一下,對於不守恆的量的改變,等於從邊界進入或離去的數量再加上內部產生的量。
看起來是廢話,但這是一句有用的廢話。
所以,你想到連續性方程了嗎?
用這句看似廢話的話,就能得到基爾霍夫電流定律,流體基本連續性方程,熱流方程……
總之,大道至簡,最簡單的思想往往有最強大的威力。
行了別拓展了,週四開始寫,現在都週五了