Codeforces 850B

题意:

给出一个序列,两种操作:

1.删除一个数,代价为x

2.给一个数+1,代价为y

求最小代价,使这个序列不为空,且所有的数的gcd>1

n<=5e5,a[i]<=1e6

 

其实思路还是很简单的。

可以发现a[i]只有1e6,那么我们直接暴力枚举修改后的数列的gcd(为下文方便,我们假设挡当前枚举的数为i)。

那么对于一个不是i的倍数的数,它就是要么删,要么就是加到最接近它的i的倍数。

所以接下来就是暴力枚举i的倍数。

如果我们当前的倍数为j,那么易发现大小[ji-1,ji-x/y]的数,就是累加到ji,大小[(j-1)i+1,ji-x/y-1]的数就是删掉。

那这个代价怎么算?

用个桶+前缀和搞下就行了。

#include<cstdio>
#include<ctime>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<string>
#define rg register
#define il inline
#define vd void
#define ll long long
#define maxn 2000010
#define N 500010
#define For(i,x,y) for (rg int i=(x);i<=(y);i++)
#define Dow(i,x,y) for (rg int i=(x);i>=(y);i--)
#define cross(i,k) for (rg int i=first[k];i;i=last[i])
using namespace std;
il ll max(ll x,ll y){return x>y?x:y;}
il ll min(ll x,ll y){return x<y?x:y;}
il ll read(){
    ll x=0;int ch=getchar(),f=1;
    while (!isdigit(ch)&&(ch!='-')&&(ch!=EOF)) ch=getchar();
    if (ch=='-'){f=-1;ch=getchar();}
    while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}
int n,Max,a[N];
ll k,l,r,x,y,z,ans,Sum,cnt[maxn],sum[maxn];
int main(){
    n=read(),x=read(),y=read(),z=x/y;
    For(i,1,n) a[i]=read(),Max=max(Max,a[i]),cnt[a[i]]++;
    For(i,1,Max*2) sum[i]=sum[i-1]+cnt[i]*i,cnt[i]+=cnt[i-1];
    ans=1000000000000000ll;
    For(i,2,max(Max,2)){
        Sum=0;
        if (Max%i==0) k=Max/i;
            else k=Max/i+1;
        if (i<=z)
            For(j,1,k){
                l=(j-1)*i,r=j*i;
                Sum+=(r*(cnt[r-1]-cnt[l])-sum[r-1]+sum[l])*y;
            }
        else 
            For(j,1,k){
                l=(j-1)*i,r=j*i;
                Sum+=(cnt[r-z-1]-cnt[l])*x+(r*(cnt[r-1]-cnt[r-z-1])-sum[r-1]+sum[r-z-1])*y;
            }
        ans=min(ans,Sum);
    }
    printf("%lld",ans);
}
原文地址:https://www.cnblogs.com/zykykyk/p/8666598.html