cf 601 E2. Send Boxes to Alice (Hard Version)

Send Boxes to Alice (Hard Version)
給定a1,a2,,ana_1,a_2,\dots,a_n,求使ax1=ax2==axt=kt=sumka_{x_1}=a_{x_2}=\dots=a_{x_t}=k,t=\frac{sum}{k} 的最小花費,其中,k是sum的因子。
顯然的,如果k1k2k_1\neq k_2,且k1k2k_1|k_2,則k=k1k=k_1時,答案小於k=k2k=k_2時的答案。
所以,枚舉因子的時候可以不用枚舉因子k的倍數。
所以,題意就是將a中的某一小段中的所有數字加到其中的一個點上,花費爲disaidis*a_i
顯然,從左到右,一段區間和大於k,那麼這一段區間必然要匯合成一個點。
該段區間花費最小時,匯合的點m座標滿足:
lm1ai+am>m+1r\sum_l^{m-1}a_i+a_m>\sum_{m+1}^r
lm1ai<am+m+1r\sum_l^{m-1}a_i<a_m+\sum_{m+1}^r
顯然,m取其他值時,答案更大。
summax=1012sum_{max}=10^{12},sum不同因子數最多爲12個。
如何快速計算一個確定的k的答案:
當i<m時,ans+=suml...ians+=sum_{l...i}
當i=m時,ans+=0ans+=0
當i>m是,ans+=sumi...r=suml...rsuml...i1=ksuml...i1ans+=sum_{i...r}=sum_{l...r}-sum_{l...i-1}=k-sum_{l...i-1}

for(int i=l;i<=r;++i){
	s=(s+a[i])%k;
	res+=min(s,k-s);
}

代碼:

#include<bits/stdc++.h>
using namespace std;
char buf[1<<20],*P1=buf,*P2=buf;
#define gc() (P1==P2&&(P2=(P1=buf)+fread(buf,1,1<<20,stdin),P1==P2)?EOF:*P1++)
#define TT template<class T>inline
TT bool read(T &x){
    x=0;char c=gc();bool f=0;
    while(c<48||c>57){if(c==EOF)return 0;f^=(c=='-'),c=gc();}
    while(47<c&&c<58)x=x*10+c-48,c=gc();
    if(f)x=-x;return 1;
}
TT bool read(T&a,T&b){return read(a)&&read(b);}
TT bool read(T&a,T&b,T&c){return read(a)&&read(b)&&read(c);}
typedef long long ll;
#define Init(a,v) memset(a,v,sizeof(a))
#define lowbit(x) (x&(-x))
const ll MAXN=1e6+8,mod=1e9+7,inf=0x3f3f3f3f;
ll n,a[MAXN],s;
ll solve(ll k){
    ll pre=0,res=0;
    for(int i=1;i<=n;++i)(pre+=a[i])%=k,res+=min(pre,k-pre);
    return res;
}
int main() {
    read(n);
    for(int i=1;i<=n;++i){
        read(a[i]);
        s+=a[i];
    }
    if(s<=1){printf("-1");return 0;}
    ll ans=1ll<<62,k;
    for(k=2;k*k<=s;++k){
        if(s%k)continue;
        while(s%k==0)s/=k;
        ans=min(ans,solve(k));
    }
    if(s>1)ans=min(ans,solve(s));
    printf("%I64d",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章