• Codeforces1254B2 Send Boxes to Alice (Hard Version)(贪心)


    题意

    n个数字的序列a,将i位置向j位置转移x个(a[i]-x,a[j]+x)的花费为(x imes |i-j|),最终状态可行的条件为所有a[i]均被K整除(K>1),求最小花费

    做法

    (sum=sumlimits a),则(K|sum)

    • (K1|sum,K2|sum),若(K1|K2),则转移到被K1整除比转移到K2更优。这个是显然的,所以最终可能成为最优解的K个数为(logsumle 40)

    对于一个枚举到的K,将(b[i]=a[i]\% K)

    对于b不为0的位置,我们类似贪心得考虑

    • 仅有前面一个位置pre不合法:因为如果有两个,在之前我们可以合成一个

    • 如果b[i]可以与前一个补满(b[i]可能还会有多余的),考虑从i转移到pre优还是pre转移到i优

    • 如果b[i]不能补满前一个,这个时候我们考虑如果能补满会补到哪里,然后更新一下pre

    Code

    比赛时调了好久,码风有点奇怪

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef long long ll;
    const LL maxn=1e6+9;
    const ll inf=0x3f3f3f3f3f3f3f3f;
    LL Read(){
    	LL x(0),f(1); char c=getchar();
    	while(c<'0' || c>'9'){
    		if(c=='-') f=-1; c=getchar();
    	}
    	while(c>='0' && c<='9'){
    		x=(x<<3)+(x<<1)+c-'0'; c=getchar();
    	}return x*f;
    }
    LL n,T,tot;
    ll sum;
    LL a[maxn],b[maxn];
    ll bel[maxn];
    bool Check(ll x){
    	for(LL i=1;i<=tot;++i) if(x%bel[i]==0) return false;
    	return true;
    }
    int main(){
    	n=Read();
    	for(LL i=1;i<=n;++i) a[i]=Read(),sum+=a[i];
    	
    	
    	/*srand(time(NULL));
    	n=1000000;
    	for(LL i=1;i<=n;++i) a[i]=rand()%1000001,sum+=a[i];
    	printf("%lld
    ",sum);*/
    
    	for(LL i=2,up=sqrt(sum);i<=up;++i) if(sum%i==0){
    	
    		if(Check(i)) bel[++tot]=i;
    		if(Check(sum/i)) bel[++tot]=sum/i;
    	}
    	if(Check(sum)) bel[++tot]=sum;
    //	printf("%d
    ",tot);
    	if(sum==0 || sum==1){
    		puts("-1"); return 0;
    	}
    	if(!tot){
    		puts("0"); return 0;
    	}
    	ll ans(inf);
    	for(LL k=1;k<=tot;++k){
    		ll x(bel[k]),nw(0),ret(0);
    		LL l(1),pre(0);
    		for(LL i=1;i<=n;++i){
    		    b[i]=a[i]%x;
    			if(!b[i]) continue;
    			if(nw){
    				if(x-nw>b[i]){
    					if((x-nw)*(i-pre)>nw*(i-pre)) ret+=nw*(i-pre),pre=i;
    					else ret+=b[i]*(i-pre);
    					nw+=b[i];
    				}else{
    					ret+=(std::min(x-nw,nw))*(i-pre);
    					nw=b[i]-(x-nw);
    					if(nw) pre=i;
    				}
    				continue;
    			}
    			nw=b[i];
    			pre=i;
    		}
    		ans=std::min(ans,ret);
    //		printf("(%lld,%lld)
    ",x,ret);
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    堆排序算法
    基数排序
    快速排序
    OpenSSL加密证书
    jobs后台任务
    at,crontab例行性任务
    dns
    ftp
    ssh
    iptables
  • 原文地址:https://www.cnblogs.com/y2823774827y/p/11895446.html
Copyright © 2020-2023  润新知