UVA11300 Spreading the Wealth 數學

前方數學警告

題目連接:https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=25&page=show_problem&problem=2275

https://www.luogu.com.cn/problem/UVA11300php

分析:

搞了這麼久終於碰到一個數學題了,這也是本蒟蒻作的第一個數學題吧,值得記念。
第一次看見這題的時候,嗯,好像是個暴力。。。而後就打了一個暴力,發現樣例過了?但本身造了組數據就有了點問題,我暴力的思路是因爲一我的只能給左邊或右邊,因而把這兩種狀況都枚舉一下,取最小值就好,但有可能跑一遍不能讓整個數組都變成我想要的那個值,故我又加了一個bool表示有無把全部數字都變成那個數,這麼寫確定T啊。
我:萬一數據水呢?
數據:我不水。
因此交上去果斷T掉了,我又想驗證一下這麼寫可不能夠,找某人代碼過來對拍了一下,而後結果是我居然把一個A掉的代碼給拍出了錯解?嗯,看來寫個暴力仍是有收穫的,我其實很想把那個代碼粘上來的,但它88行有點長我沒存不當心弄丟了,來看一下正解吧。ios

首先我最開始發現的那個性質是沒有問題的,即每一個人只會被它周圍的人影響,一旦他周圍的人的轉移硬幣數肯定了,那麼這我的的轉移硬幣數也可求,因而受這一點啓發,不妨設\(x_i\)表示第\(i\)我的給第\(i-1\)我的的硬幣數,當\(i==1\)時,爲給\(n\)的硬幣數。因爲最後要求全部人金幣同樣(共產主義),因此最後每一個人的金幣數量是肯定的,即\(M=tot/n\),也就是說每一個人最後都是\(M\),因而有\(A_i+x_{i+1}-x_i=M\),就是原來有的加上獲得的減去失去的等於最終的,光看這個式子,這啥也看不出來啊,因此繼續觀察這個式子,發現它跟周圍的式子相加會出現一個神奇的結果,咱們加一下看看。
\(A_1+x_2-x_1=M\) 1
\(A_2+x_3-x_2=M\) 2
\(A_3+x_4-x_2=M\) 3
1+2得\(A_1+A_2+x_3-x_1=M*2\) -> \(x_3=x_1-(A_1+A_2-M*2)\)
1+2+3得\(x_4=x_1-(A_1+A_2+A_3-M*3)\)
………………
不難概括得出\(x_n=x_1-\)\(\sum_{i=1}^n(A_{i-1}+(i-1)*M\)\()\)
有這個式子咱們就能夠很簡單困難的求出答案了,設\(B_i=\)\(\sum_{i=1}^n(A_{i-1}+(i-1)*M\)\(B\)數組能夠由遞推獲得,那麼問題就簡化成了在數組\(B\)中求一個數,使得剩餘數到它的距離之和最小,答案是中位數,證實以下。

假設\(a_1,a_2,a_3,a_4,a_5\)有序,則\(a_3\)是中位數無疑,咱們用線段來表示點與點之間的距離,能夠看出藍色框框內的線段是必不可少的,無論怎麼劃分都必需要有,若是這個點在\(a_3\)的左邊,那麼就會多出粉色的一截,在右邊就會多出紅色的一截,因此在中位數時是最小的。
那偶數呢?偶數不是有兩個中位數嗎?偶數時也畫出一個這樣的圖,會發現當點在兩個中位數之間距離都是同樣的,而且是最小,因此爲了方便,每次中位數都取\(n+1>>1\)就能夠了。數組

中位數怎麼找呢,排序一遍能夠,但複雜度是\(O(nlogn)\)的,若是要使數組有序,那麼確定是要\(sort\)的,這裏只求中位數,因此能夠用\(nth\)\(element\),這個函數的用法是\(nth\)\(element(A+a,b,A+c)\),表示將數組A的區間\([a,c)\)中第b大的數放在b位置上,不保證數組有序,但比這個數大的都在它前邊,比它小的在它後邊,平均時間複雜度\(O(n)\)函數

#include<iostream>
#include<cmath>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e6+10;
ll a[N],b[N];
int main(){
    ios::sync_with_stdio(false);
    int n;
    while(cin>>n){
        ll tot=0;
        for(int i=1;i<=n;i++)
            cin>>a[i],tot+=a[i],b[i]=0;
        tot/=n;
        for(int i=2;i<=n;i++)
            b[i]=b[i-1]+a[i]-tot;
        int mid=n+1>>1;
        nth_element(b+1,b+mid,b+n+1);
        ll temp=b[mid],res=0;
        for(int i=1;i<=n;i++)
            res+=abs(temp-b[i]);
        cout<<res<<'\n';
    }
}

tips:cout<<endl的確比cout<<'\n'慢一點。spa

相關文章
相關標籤/搜索