【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个位置的数字进行; 将 ...
随机推荐
- linux --备份oracle
1.exp\imp 导入导出命令使用exp username/pwd@sid file=path.dmp owner=user 不导出表数据:rows=n举例:exp iflashbuy/qwerwh ...
- HUDSON(Java开发的一种持续集成工具)
Hudson是Jenkins的前身,是基于Java开发的一种持续集成工具,用于监控程序重复的工作,包括: 1.持续的软件版本发布/测试项目. 2.监控外部调用执行的工作. Hudson的特性 1.易于 ...
- android源码在线查看
http://grepcode.com/project/repository.grepcode.com/java/ext/com.google.android/android/
- 【java基础】 如何导入外部jar包
转:from http://www.zhihu.com/question/20311561 有两种常用的方法. 1. 以外部包(External Archives)的形式导入. 在默认位于 Eclip ...
- VS2013无法启动 IIS Express Web解决办法
不要勾选[覆盖应用程序根URL(U)],或让[覆盖应用程序根URL(U)]下面的输入框和上面的输入框的地址一样! 使用VS2013有一段时间了,因前期都是编写C/S程序,没有使用到B/S调试器.前几日 ...
- Window.open()方法参数详解
Window.open()方法参数详解 1, 最基本的弹出窗口代码 window.open('page.html'); 2, 经过设置后的弹出窗口 window.open('page.html ...
- Struts登录
- ndk开发教程以及问题解决方案
一.NDK产生的背景 Android平台从诞生起,就已经支持C.C++开发.众所周知,Android的SDK基于Java实现,这意味着基于Android SDK进行开发的第三方应用都必须使用Java语 ...
- scala学习之第三天:数组的特性与使用技巧
1.数组 Scala数组与Scala序列是兼容的 - 在需要Seq[T]的地方可由Array[T]代替.最后,Scala数组支持所有的序列操作. 隐式转换 方法1:通过scala.collection ...
- [资料收集]MySQL在线DDL工具pt-online-schema-change
MySQL在线DDL工具pt-online-schema-change pt-online-schema-change使用说明(未完待续) 官网