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.

input
The first line of the input contains integers n, a and b (1 ≤ n ≤ 1 000 000, 0 ≤ a, b ≤ 10^9) — 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 ≤ 10^9) — elements of the array.

output
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.

题意
给一个序列,含n个元素,可以进行两种操作:
1、删除一段连续区间的元素,代价为区间长度*a,但不能将所有元素删除;
2、对一部分元素进行+1或-1,每次修改代价为b;
问,使全部元素的最大公因数大于1需要的最小代价

题解
由于不能删除所有的元素,故a1、an至少有一个元素存在,全部元素的公因数必然含有a1,a1-1,a1+1,an,an-1,an+1的某个质因子,枚举a1,a1-1,a1+1,an,an-1,an+1的所有质因子,进行dp
dp[i][0] 表示当前元素不在删除的序列中,且在处理第i个元素前,未删除过元素
dp[i][1] 表示当前元素包含于删除的序列中(第i个元素是删除序列的第一个元素或中间元素,即第i-1个元素可能在删除序列中,也可能不在删除序列中)
dp[i][2] 表示当前元素不在删除序列中,且删除的序列已经确定(即第i-1个元素可能是删除序列的最后一个,或不在删除序列中)


得到状态转移方程为:

dp[i][0] = dp[i-1][0] + cost
dp[i][1] = min(dp[i-1][0],dp[i-1][1]) + a
dp[i][2] = min(dp[i-1][1],dp[i-1][2]) + cost

若 ai 可以整除当前枚举的质数,则cost = 0
若 ai+1 或 ai-1 可以整除当前枚举的质数,则cost = b


代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<math.h>

using namespace std;

#define ll long long

const int maxn = 1e6+100;
const int d[3] = {0,1,-1};
int a,b,last,num,n,c[maxn],p[maxn];
ll ans,dp[maxn][3];

void get_prime(int x)
{
    for (int i=2;i*i<=x;i++)
    {
        if (x % i == 0)
            p[num++] = i;
        while (x % i == 0) x /= i;
    }
    if (x != 1) p[num++] = x;
    return;
}

void solve(int pr)
{
    memset(dp,0,sizeof(dp));
    for (int i=1;i<=n;i++)
    {
        ll cost = 1e10;
        if (c[i] % pr == 0)
            cost = 0;
        else
            if ((c[i]-1)%pr==0 || (c[i]+1)%pr==0)
                cost = b;
        dp[i][0] = dp[i-1][0] + cost;
        dp[i][1] = min(dp[i-1][0],dp[i-1][1]) + a;
        dp[i][2] = min(dp[i-1][1],dp[i-1][2]) + cost;
    }
    for (int i=0;i<3;i++)
        ans = min(dp[n][i],ans);
    return;
}

int main()
{
    scanf("%d %d %d",&n,&a,&b);
    for (int i=1;i<=n;i++)
        scanf("%d",&c[i]);
    for (int k=0;k<3;k++)
        get_prime(c[1]+d[k]);
    for (int k=0;k<3;k++)
        get_prime(c[n]+d[k]);

    sort(p,p+num);
    last = 0;
    ans = 1e15;
    for (int i=0;i<num;i++)
        if (p[i] != last)
        {
            solve(p[i]);
            last = p[i];
        }
    printf("%I64d\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值