Codeforces Round #432 (Div. 2) D 850B Arpa and a list of numbers(gcd 枚举)

41 篇文章 0 订阅
37 篇文章 0 订阅

一看到感觉是dp,但是dp也太难了呀,然后就滚了.
gcd的问题很多时候都是枚举,因为gcd其实是很难递推的一个东西,所以观察一下ai的范围,大概也能猜到.
那么我们枚举每一个gcd,当然,是素数啦,然后发现每一个gcd我们数列的值可以O(n)算出来,可是素数有很多,这样不够,那么我们怎么样更快呢,就是可以利用一个区间的思想,假设x/y=delta,也就是说,假设p是gcd的一个倍数,那么在p-delta~p的范围内的数,他们要变成p,而在p-gcd~p-delta-1范围内的数,就是要删除,对吧?所以我们预处理一下,就可以在O(1)内得到要删除的数字数量和要加的数字之和(注意两者不同,要处理两个数组).感觉这里很奇妙的就是以前的前缀和都是以下标来做数组,但是这里直接是以值来做数组,这样好处就是假如用下标,那么要找到是logn,但这里ai小,所以直接用,可以O(1)得到,第一次见呢.
(感觉D题还是太难了,我还是太菜了,写起来真的各种难,留下我丑陋的调试痕迹好了)

/*  xzppp  */
#include <iostream>
#include <vector>
#include <cstdio>
#include <string.h>
#include <algorithm>
#include <queue>
#include <map>
#include <string>
#include <cmath>
#include <bitset>
#include <iomanip>
using namespace std;
#define FFF freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define MP make_pair
#define PB push_back
typedef long long  LL;
typedef unsigned long long ULL;
typedef pair<int,int > pii;
typedef pair<double,double > pdd;
typedef pair<double,int > pdi;
const int MAXN = 1e6+17;
const int MAXM = 20;
const int MAXV = 2*1e3+17;
const int INF = 0x7fffffff;
const LL INFF = 0x7fffffffffffffff;
const int MOD = 1e9+7;
LL prime[MAXN];
LL vis[MAXN];
LL cnt;
void get(){
    memset(vis, 0, sizeof(vis));
    cnt=0;
    for(int i=2;i<MAXN;i++){
        if(!vis[i]){
            prime[++cnt]=i;
            for(int j=i+i;j<MAXN;j+=i){
                vis[j]=1;
            }
        }
    }
}
LL a[2*MAXN],c[2*MAXN],tms[2*MAXN],sum[2*MAXN];
int main()
{
    #ifndef ONLINE_JUDGE 
    FFF
    #endif
    get();
    LL n,x,y,gst= 0 ;
    cin>>n>>x>>y;
    for (int i = 0; i < n; ++i)
    {
        scanf("%lld",a+i);
        tms[a[i]]++;
        sum[a[i]]+=a[i];
        gst = max(gst,a[i]);
    }
    for (int i = 1; i <= 2*MAXN; ++i)
    {
        tms[i]+=tms[i-1];
        sum[i]+=sum[i-1];
    }
    LL ans = INFF,delta = x/y,ori = delta;
    for (int i = 1; i <= cnt; ++i)
    {
        //cout<<prime[i]<<endl;
        if(prime[i]>2*gst) break;
        LL p = prime[i];
        LL del=0,inc=0,sinc=0,temp=0;
        for (LL j = p; j <= gst+p; j+=p)
        {
            if(delta<p-1)
            {
                del =  tms[j-delta-1] - tms[j-p];
                inc =  sum[j-1] - sum[j-delta-1];
                sinc = tms[j-1] - tms[j-delta-1];
                //if(del<0) cout<<j-delta-1<<" asd "<<j-p<<endl;
            }
            else
            {
                inc = sum[j-1] - sum[j-p];
                sinc = tms[j-1] - tms[j-p];
                //if(inc<0) cout<<j-1<<" sec "<<j-p<<endl;
                //cout<<j<<"  "<<inc<<"  "<<sinc<<endl;  
            }
            //cout<<j<<"  "<<del<<"  "<<inc<<"  "<<sinc<<endl;
            temp += del*x+(sinc*j-inc)*y;
            //cout<<temp<<endl;
        }
        //LL temp = del*x + (sinc*p-inc)*y;
        //cout<<"ans"<<temp<<endl;
        ans = min(temp,ans);
    }
    cout<<ans<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值