1. 程式人生 > >P2512 [HAOI2008]糖果傳遞&&P3156 [CQOI2011]分金幣&&P4016 負載平衡問題

P2512 [HAOI2008]糖果傳遞&&P3156 [CQOI2011]分金幣&&P4016 負載平衡問題

algorithm -m for href www %d pro ref tdi

P2512 [HAOI2008]糖果傳遞

第一步,當然是把數據減去平均數,然後我們可以得出一串正負不等的數列

我們用sum數組存該數列的前綴和。註意sum[ n ]=0

假設為鏈,那麽可以得出答案為abs( sum[ 1 ] )+abs( sum[ 2 ] )+...+abs( sum[ n ] )

但是題目說的是環

我們設在第 k 個人處斷開環成鏈。 那麽答案為
abs( sum[ k+1 ] - sum[ k ] )+abs( sum[ k+2 ] - sum[ k ] )+...+abs( sum[ n ] - sum[ k ] )+abs( sum[ n ] - sum[ k ] + sum[ 1 ])+abs( sum[ n ] - sum[ k ] + sum[ 2 ])+...+abs( sum[ n ] - sum[ k ] + sum[ k ])

代入sum[ n ]=0後,得abs( sum[ k+1 ] - sum[ k ] )+abs( sum[ k+2 ] - sum[ k ] )+...+abs( sum[ n ] - sum[ k ] )+abs( sum[ 1 ] - sum[ k ] )+abs( sum[ 2 ] - sum[ k ] )+...+abs( sum[ k ] - sum[ k ] )

=abs( sum[1~n] - sum[k] )

我們把 sum[1~n] 扔到數軸上,發現問題變成:找出一個點,使它到其他點的距離最小。顯然這個點是中位數。

end.

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cctype>
#include<algorithm>
using namespace std;
typedef long long ll;
inline ll Get(){
    char c=getchar(); ll x=0;
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x;
}
ll ans,a[1000002],sum[1000002]; int n;
int main(){
    scanf("%d",&n);
    ll tot=0;
    for(int i=1;i<=n;++i) a[i]=Get(),tot+=a[i];
    tot/=n;
    for(int i=1;i<=n;++i) a[i]-=tot,sum[i]=a[i]+sum[i-1]; //減平均數,求前綴和
    sort(sum+1,sum+n+1);
    ll mid=sum[(n+1)>>1]; //排序後找中位數
    for(int i=1;i<=n;++i) ans+=abs(sum[i]-mid);
    printf("%lld",ans);
    return 0;
}

P2512 [HAOI2008]糖果傳遞&&P3156 [CQOI2011]分金幣&&P4016 負載平衡問題