BZOJ1045 [HAOI2008]糖果傳遞 && BZOJ3293 [Cqoi2011]分金幣
Description
有n個小朋友坐成一圈,每人有ai個糖果。每人只能給左右兩人傳遞糖果。每人每次傳遞一個糖果代價為1。
Input
第一行一個正整數nn<=1'000'000,表示小朋友的個數. 接下來n行,每行一個整數ai,表示第i個小朋友得到的糖果的顆數.Output
求使所有人獲得均等糖果的最小代價。
Sample Input
41254Sample Output
4Solution
數學題
1045和3293是重題所以就放一起了,其實還有lrj藍書上面的一道題也和這個一樣(UVA的)
首先我們設每個人最後擁有的糖果數為$m$
那麼很顯然這個$m$是可以求出來的,$$m=\frac{\sum _{i=1}^{i<=n}A_i}{n}$$
再設一下,$xi$代表每個人傳給了自己左邊的人$xi$個糖果(對於$x1$,代表第一個人傳給最後一個人$x1$個糖果(環形))
考慮第i個人,可以得到一個很顯而易見的方程:$A_i - x_i + x_{i+1} = m$
為什麼這個方程不用考慮左邊的人傳給這個人的情況?假設第$1$個人傳給第$2$個人$3$個糖果,第$2$個人傳給第$1$個人5個糖果,其實也就相當於,第$2$個人傳給第$1$個人$2$個糖果,所以是不用考慮這個情況的(如果$1$傳給$2$的牌比$2$傳給$1$的多,那麼$x2$則為負數)
同理可以得到一大堆的方程(其實就是把$1$~$n$分別代入上面的$i$)
我們可以嘗試著解方程
然後會發現這方程是解不出來的
但是我們發現了一個點,可以拿x1表示這一大堆的其他的xi
現在我們設一個C陣列,規定$C_i=C_{i-1}+A_i-m$
$a_1-x_1+x_2=m$化為$x_2=m-a_1+x_1=x_1-C_1$
同理,$a_2-x_2+x_3=m$化為
$x3$$=m-a2+x2$$=2*m-a2-a1+x1$$=x1-C1-a2+m$$=x1-C2$
於是我們就可以得到$n$個形似$xi=x1-Ci$的式子
好了我們在距離正解的路上已經邁出了一大步
考慮我們這$n-1$個等式能幹啥
想想題目,我們想要傳遞的糖果數量儘可能少,也就是說,我們要讓$\sum{abs(x)}$最小
然後再套一下之前的方程我們就可以把這個$\sum{abs(x)}$改寫成$\sum{abs(x_1)+abs(x_1-C_1)+abs(x_2-C_2)···+abs(x_1-C_{n})}$
於是現在的問題就變成了,我們需要一個$x1$讓$\sum{abs(x)}$最小
把這個玩意,對映到數軸上面,你就會發現一個神奇的東西
是的,就是中位數
怎麼證明?
隨便找一個點,假設這個點左邊的點的數量多於右邊的點的數量,那麼肯定不是最優的,要向左移動(設它的左邊有$l$個點,右邊有$r$個點)(如果它向左移動了$t$個單位長度,假設還沒碰到其他點,那麼它距離左邊的點少了$tl$的距離,距離右邊多了$tr$的距離,總的距離事實上減少了$(l-r)t$個單位長度的距離,更優)
所以,使這個$\sum{abs(x)}$最小的$x_1$,一定會是$C$陣列的中位數
所以得到這個$x1$之後,就可以推出這個$\sum{abs(x)}$的值了,答案也就出來了
嗯,這道題還卡$long \ long$,記得注意一下
挺好的一道數學題,挺思維的,程式碼難度也不大
#include <bits/stdc++.h> using namespace std ; #define ll long long #define N 1000100 int n ; ll a[ N ] , c[ N ] , sum = 0 , ans = 0 ; int main() { scanf( "%d" , &n ) ; for( int i = 1 ; i <= n ; i ++ ) { scanf( "%lld" , &a[ i ] ) ; sum += a[ i ] ; } ll m = sum / n ; for( int i = 1 ; i <= n ; i ++ ) { c[ i ] = c[ i - 1 ] + a[ i ] - m ; } sort( c + 1 , c + n + 1 ) ; ll t = c[ n & 1 ? ( n + 1 ) >> 1 : n >> 1 ] ; for( int i = 1 ; i <= n ; i ++ ) { ans += abs( c[ i ] - t ) ; } printf( "%lld\n" , ans ) ; return 0 ; }