【CodeForces 624D】Array GCD
You are given array ai of length n. You may consecutively apply two operations to this array:
- remove some subsegment (continuous subsequence) of length m < n and pay for it m·a coins;
- change some elements of the array by at most 1, and pay b coins for each change.
Please note that each of operations may be applied at most once (and may be not applied at all) so you can remove only one segment and each number may be changed (increased or decreased) by at most 1. Also note, that you are not allowed to delete the whole array.
Your goal is to calculate the minimum number of coins that you need to spend in order to make the greatest common divisor of the elements of the resulting array be greater than 1.
The first line of the input contains integers n, a and b (1 ≤ n ≤ 1 000 000, 0 ≤ a, b ≤ 109) — the length of the array, the cost of removing a single element in the first operation and the cost of changing an element, respectively.
The second line contains n integers ai (2 ≤ ai ≤ 109) — elements of the array.
Print a single number — the minimum cost of changes needed to obtain an array, such that the greatest common divisor of all its elements is greater than 1.
3 1 4
4 2 3
1
5 3 2
5 17 13 5 6
8
8 3 4
3 7 5 4 3 12 9 4
13
In the first sample the optimal way is to remove number 3 and pay 1 coin for it.
In the second sample you need to remove a segment [17, 13] and then decrease number 6. The cost of these changes is equal to 2·3 + 2 = 8 coins.
题意
给你n个数,可以执行两种操作最多各一次,一是移除一个连续的序列不可移动整个数列,代价是序列长度*a,二是选一些数改变1,即可以有的加一,有的减一,代价是数字个数*b
最终使得剩下所有数字的最大公约数大于1,求最少代价
分析
因为不能移除所有的数,所以a[1]和a[n]至少会有一个留下来,所以要在a[1]-1、a[1]、a[1]+1、a[n]-1、a[n]、a[n]+1六个数的所有质因数里找最大公约数。
找出t的所有质因子是j从2到根号t,如果t能整除j,j就是一个质因数,存起来,然后t一直除以j,除到不能整除为止,最后判断一下剩下的数如果大于1,那应该就是它本身了,那说明他就是一个素数,也要存起来。存的时候要注意不要重复。这样才不会超时,如果做素数表然后一个个素数去判断是不是它的因数就会超时。
找好后,对每个质因数,进行尺扫(或者叫尺取,还有种方法是DP(待补充)),左边扫到右边,预处理一下前i个数里有几个需要改变(bnum[i]),有几个必须移除的(anum[i])。
扫的时候,移除L到R这段区间的代价为aCost,不移除的代价为bCost,当bCost<=aCost时说明不用移除更划算,于是L更新为R+1,并且存下修改前面的数需要的代价oldbCost,否则计算一下只移除这段区间,其他区间通过修改达到目标,需要多少代价(oldbCost + aCost + ( bnum[n] - bnum[R] ) * b),然后更新答案。算代价的时候如果一个数必须移除,那修改的代价设为无穷。
代码
#include<stdio.h>
#include<algorithm>
#define ll long long
#define N 1000005
using namespace std;
ll n,a,b,m[N],p[N],bnum[N],anum[N],primeFactorNum,minc=1e18; void savePrime(ll a)
{
int noSaved=;
for(int i=; i<primeFactorNum && noSaved; i++)
if(a==p[i])
noSaved=;
if(noSaved)
p[primeFactorNum++]=a;
} void FindPrimeFactor(ll a)
{
for(int i=-; i<=; i++)
{
int j,t=a+i;
for(j=; j*j<=t; j++)
{
if(t%j==)
{
savePrime(j);
while(t%j==)t/=j;
}
}
if(t>)
savePrime(t);
}
} ll cost(ll num,ll factor)
{
if(num%factor==)
return ;
if( (num+) % factor && (num-) % factor )
return 1e17;//+1或-1都不能整除factor
return b;
} void solve(ll factor)
{
ll L=,R,bCost=,aCost=,oldbCost=; while( cost(m[L],factor) == && L <= n) L++;//左边过滤掉不需要改动的连续序列
if(L == n+)
{
minc=;
return;
}
R = L-; bnum[] = anum[] = ; for(int i=; i<=n; i++)
{
ll tmp=cost(m[i],factor); if(tmp == b)
bnum[i] = bnum[i-]+;
else
bnum[i] = bnum[i-]; if(tmp == 1e17)
anum[i] = anum[i-]+;
else
anum[i] = anum[i-];
} if(anum[n] == )
minc = min( minc, bnum[n] * b ); while(R<n)
{
aCost+=a;
R++;
if(bCost<1e17)
bCost+=cost(m[R],factor);
if(bCost<=aCost)
{
L=R+;
oldbCost+=bCost;
bCost=aCost=;
}
else
{
if(anum[n]-anum[R]==)
minc = min( minc , oldbCost + aCost + ( bnum[n] - bnum[R] ) * b );
}
}
}
int main()
{
scanf("%I64d%I64d%I64d",&n,&a,&b);
for(int i=; i<=n; i++)
scanf("%I64d",&m[i]); FindPrimeFactor(m[]);
FindPrimeFactor(m[n]); for(int i=; i<primeFactorNum && minc; i++)
solve(p[i]); printf("%I64d",minc);
return ;
}
2017/8/14,偶然看到自己这篇超长题解和代码,这题没那么烦吧。。因为六个月前的训练赛又挂了这题,然后重写的代码:
当时写得变量名真色。。我快看不懂了。。no代表一个都没删除的最小代价,be表示删除了第i个的最小代价,t就是删除了第i-1个的最小代价,ov就是有删除过但第i-1个未必删除了的最小代价。算是dp的思路。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define ll long long
#define N 1000005
#define inf (1LL<<60)
using namespace std;
int n,c[N],d[],pr[N],cnt;
ll a,b;
int main() {
scanf("%d%lld%lld",&n,&a,&b);
for(int i=;i<=n;i++)
scanf("%d",&c[i]);
d[]=c[]-,d[]=c[]+;
d[]=c[n]-,d[]=c[n]+;
d[]=c[];d[]=c[n];
for(int i=;i<;i++){
for(int j=;j*j<=d[i];j++)
if(d[i]%j==){
pr[cnt++]=j;
while(d[i]%j==)
d[i]/=j;
}
if(d[i]>)pr[cnt++]=d[i];
}
sort(pr,pr+cnt);
int m=;
pr[m++]=pr[];
for(int i=;i<cnt;i++)if(pr[i]!=pr[i-])pr[m++]=pr[i];
ll ans=inf;
for(int i=;i<m;i++){
ll no=,be=,ov=;
for(int j=;j<=n;j++){
ll t=be;
be=min(no+a,be+a);
if(c[j]%pr[i]){
if((c[j]+)%pr[i]==||(c[j]-)%pr[i]==)
{
no+=b;
ov=min(min(ov+b,t+b),t+a);
}else{
no=inf;
ov=t+a;
}
}else{
ov=min(t,ov);
}
}
ans=min(ans,min(min(no,be),ov));
}
printf("%lld\n",ans);
return ;
}
【CodeForces 624D】Array GCD的更多相关文章
- 【Codeforces 664A】 Complicated GCD
[题目链接] 点击打开链接 [算法] gcd(a,a+1) = 1 所以当a = b时,答案为a,否则为1 [代码] #include<bits/stdc++.h> using names ...
- 【codeforces 797E】Array Queries
[题目链接]:http://codeforces.com/problemset/problem/797/E [题意] 给你一个n个元素的数组; 每个元素都在1..n之间; 然后给你q个询问; 每个询问 ...
- 【codeforces 803C】Maximal GCD
[题目链接]:http://codeforces.com/contest/803/problem/C [题意] 给你一个数字n;一个数字k; 让你找一个长度为k的序列; 要求这个长度为k的序列的所有数 ...
- 【Codeforces 1034A】Enlarge GCD
[链接] 我是链接,点我呀:) [题意] 题意 [题解] 设原来n个数字的gcd为g 减少某些数字之后 新的gcd肯定是g的倍数 即gx 我们可以枚举这个x值(x>=2) 看看原来的数字里面有多 ...
- 【Codeforces 1108E1】Array and Segments (Easy version)
[链接] 我是链接,点我呀:) [题意] 题意 [题解] 枚举最大值和最小值在什么地方. 显然,只要包含最小值的区间,都让他减少. 因为就算那个区间包含最大值,也无所谓,因为不会让答案变小. 但是那些 ...
- 【codeforces 415D】Mashmokh and ACM(普通dp)
[codeforces 415D]Mashmokh and ACM 题意:美丽数列定义:对于数列中的每一个i都满足:arr[i+1]%arr[i]==0 输入n,k(1<=n,k<=200 ...
- 【UOJ#33】【UR#2】树上GCD 有根树点分治 + 容斥原理 + 分块
#33. [UR #2]树上GCD 有一棵$n$个结点的有根树$T$.结点编号为$1…n$,其中根结点为$1$. 树上每条边的长度为$1$.我们用$d(x,y)$表示结点$x,y$在树上的距离,$LC ...
- 【UOJ#33】【UR #2】树上GCD(长链剖分,分块)
[UOJ#33][UR #2]树上GCD(长链剖分,分块) 题面 UOJ 题解 首先不求恰好,改为求\(i\)的倍数的个数,最后容斥一下就可以解决了. 那么我们考虑枚举一个\(LCA\)位置,在其两棵 ...
- 【codeforces 798C】Mike and gcd problem
[题目链接]:http://codeforces.com/contest/798/problem/C [题意] 给你n个数字; 要求你进行若干次操作; 每次操作对第i和第i+1个位置的数字进行; 将 ...
随机推荐
- vector中pair的排序方法
直接上代码: bool judge(const pair<int,char> a, const pair<int ,char> b) { return a.first<b ...
- Linux命令学习-mpstat
mpstat 用于获取多个 CPU 相关统计信息的有用的命令是 mpstat.下面是一个示例输出: # mpstat -P ALL 5 2 Linux 2.6.9-67.ELsmp (oraclera ...
- java11-5 String类的转换功能
String的转换功能: byte[] getBytes():把字符串转换为字节数组. char[] toCharArray():把字符串转换为字符数组. static String valueOf( ...
- C/C++ 常用工具集
1. c++filt //注意:就是这个名字 "c++file". 能把c++的函数签名转换成代码形参格式: 如:# c++filt _ZNSt4priv17_Rb_tree_i ...
- Centos7下修改默认网卡名(改为eth0)的操作记录
安装好centos7版本的系统后,发现默认的网卡名字有点怪,为了便于管理,可以手动修改.下面对centos7版本下网卡重命名操作做一记录:1)编辑网卡信息[root@linux-node2~]# cd ...
- 19SpringMvc_在业务控制方法中收集List集合中包含JavaBean参数
本文要实现的功能是给一张表单:
- WMI入门
转:http://www.cnblogs.com/ceachy/archive/2013/03/21/WMI_What.html WMI入门(一):什么是WMI WMI出现至今已经二十多年了,但很多人 ...
- 教你10分钟内在Windows上完成Rails开发环境的安装和配置
原文:http://www.cnblogs.com/tambor/archive/2011/12/25/rails_anzhuang_railsinstaller.html 一般来说,Windows开 ...
- Android studio导入eclipse项目混淆打包出错
将proguard-android.txt复制一份重命名成proguard-rules.pro,且在build.gradle添加 release { minifyEnabled ...
- FTP登录/目录破解
l临时简单的验证,还需要优化... 登录: import ftplib usr = open('usr.txt','r') ftp = ftplib.FTP("*************&q ...