您的位置:首页 > 其它

Codeforces 851D Arpa and a list of numbers Round #432 (Div. 2

2017-09-06 11:53 666 查看
题意:

给你n个数,你有两个操作:

①将某个数删掉,消耗为x

②将某个数+1,消耗为y,这个操作可以对某个数多次使用

而你的目的是让最后n个数的Gcd不为1,求最小消耗

思路:

思路懒得打了,看下面这篇博客的吧

http://blog.csdn.net/mengxiang000000/article/details/77850732

我们知道,Gcd的结果是素数是最好的,所以我们首先筛出素数,1e6内的素数个数约为8w(8e4)个。

然后考虑对应数字是删除还是增添。很显然我们这里可以Dp去做,但是时间复杂度很爆炸,所以考虑X和Y两种操作之间的关系。

①不难想到,如果X<Y的话,我们就不用进行Y操作了。

②所以我们设定move=X/Y表示我们最多对一个数进行增添数字的次数,如果超过了这个次数,我们不如将其删除来的划算。

接下来考虑如何计算结果。首先我们肯定要O(8e4)去枚举答案,接下来考虑,既然我们已经知道了move这个信息,那么对于当前枚举的素数p,我们有一个区间【p,2p】的话,我们肯定能够通过move这个信息找到区间的一个分界点,使得【p~分界点】的数我们都将其删除,使得【分界点~2p】的数字都增添变成2p.

那么这里我们只要枚举出来一个素数p,然后每一次计算之后将区间移动p个长度即可。整体时间复杂度O(Len*LogLen)这里Len表示素数的个数。

具体实现细节都在代码的注释里

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
int a[3000000];
long long num[3000000]= {0};
long long  ssum[3000000]= {0};
int main()
{
int n,x,y;
// cout<<__gcd(1000000,5000001)<<endl;

while(cin>>n>>x>>y)
{
int p = x/y;
long long ans = (long long)x*n;
memset(num,0,sizeof(num));
memset(ssum,0,sizeof(ssum));
int data;
for(int i=1; i<=n; i++)
{
scanf("%d",&data);
num[data]++;
ssum[data]+=data;
}
for(int i=1; i<=2000000; i++)//配合下面计算
{
num[i]+=num[i-1];
ssum[i]+=ssum[i-1];
}

for(int i=2; i<=1000000; i++) //枚举这一群数的gcd,
//理论上都是素数,但为了简便,枚举了所有数
{
long long sum = 0,nsum1,nsum2,vsum;
for(int j=i; j<=1000000+i; j+=i)//此处要特别注意,因为
//j+1e6 更换成2e6其实也可以 ,考虑这样一组数据,
// 假设1e6 换成100  当i是94的时候,(1,94)(94,188)这时候
//因为j代表的是右边界,所以区间话(94,188)超过了1e6所以跳出
//但是这个区间内的数也是应该计算的,所以要跑到2e6
{
int fen = max(j-i+1,j-p);
//  对于每个区间(j-i+1,j)找到他的分界点
vsum = ssum[j]-ssum[fen-1];//需要增加1的数的个数
nsum1 = num[j]-num[fen-1];//需要增加1的的数的总和
//j*vsum为这些数需要增加到的总和
nsum2 = num[fen-1]-num[j-i];//统计需要删除的个数
sum+= nsum2*x;
sum+=(j*nsum1-vsum)*y;//统计本区间内需要增加的数值
}
// cout<<sum<<endl;
ans = min(ans,sum);
}
cout<<ans<<endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  codeforces gcd 前缀和