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

Input

The first line of the input contains integers na 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.

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.

Sample test(s)

input

3 1 44 2 3

output

1

input

5 3 25 17 13 5 6

output

8

input

8 3 43 7 5 4 3 12 9 4

output

13

Note

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=1;
    for(int i=0; i<primeFactorNum && noSaved; i++)
        if(a==p[i])
            noSaved=0;
    if(noSaved)
        p[primeFactorNum++]=a;
}

void FindPrimeFactor(ll a)
{
    for(int i=-1; i<=1; i++)
    {
        int j,t=a+i;
        for(j=2; j*j<=t; j++)
        {
            if(t%j==0)
            {
                savePrime(j);
                while(t%j==0)t/=j;
            }
        }
        if(t>1)
            savePrime(t);
    }
}

ll cost(ll num,ll factor)
{
    if(num%factor==0)
        return 0;
    if( (num+1) % factor && (num-1) % factor )
        return 1e17;//+1或-1都不能整除factor
    return b;
}

void solve(ll factor)
{
    ll L=1,R,bCost=0,aCost=0,oldbCost=0;

    while( cost(m[L],factor) == 0 && L <= n) L++;//左边过滤掉不需要改动的连续序列
    if(L == n+1)
    {
        minc=0;
        return;
    }
    R = L-1;

    bnum[0] = anum[0] = 0;

    for(int i=1; i<=n; i++)
    {
        ll tmp=cost(m[i],factor);

        if(tmp == b)
            bnum[i] = bnum[i-1]+1;
        else
            bnum[i] = bnum[i-1];

        if(tmp == 1e17)
            anum[i] = anum[i-1]+1;
        else
            anum[i] = anum[i-1];
    }

    if(anum[n] == 0)
        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+1;
            oldbCost+=bCost;
            bCost=aCost=0;
        }
        else
        {
            if(anum[n]-anum[R]==0)
                minc = min( minc , oldbCost + aCost + ( bnum[n] - bnum[R] ) * b );
        }
    }
}
int main()
{
    scanf("%I64d%I64d%I64d",&n,&a,&b);
    for(int i=1; i<=n; i++)
        scanf("%I64d",&m[i]);

    FindPrimeFactor(m[1]);
    FindPrimeFactor(m[n]);

    for(int i=0; i<primeFactorNum && minc; i++)
        solve(p[i]);

    printf("%I64d",minc);
    return 0;
} 
时间: 2024-10-29 19:06:15

【CodeForces 624D】Array GCD的相关文章

【codeforces 718E】E. Matvey&#39;s Birthday

题目大意&链接: http://codeforces.com/problemset/problem/718/E 给一个长为n(n<=100 000)的只包含‘a’~‘h’8个字符的字符串s.两个位置i,j(i!=j)存在一条边,当且仅当|i-j|==1或s[i]==s[j].求这个无向图的直径,以及直径数量. 题解:  命题1:任意位置之间距离不会大于15. 证明:对于任意两个位置i,j之间,其所经过每种字符不会超过2个(因为相同字符会连边),所以i,j经过节点至多为16,也就意味着边数至多

【codeforces 415D】Mashmokh and ACM(普通dp)

[codeforces 415D]Mashmokh and ACM 题意:美丽数列定义:对于数列中的每一个i都满足:arr[i+1]%arr[i]==0 输入n,k(1<=n,k<=2000),问满足[数列长度是k && 数列中每一个元素arr[i]在1~n之间 && 数列中元素可以重复]的数列有多少个?结果对10^9+7取余 解题思路:dp[i][j]表示长度是j,最后一位是i的种数 if(kk%i==0) dp[kk][j+1]+=dp[i][j] 1 #i

【Codeforces 368A】Brain&#39;s Photos 水题

黑白灰都是#Black&White #include <cstdio> int n,m; int main() { scanf("%d%d",&n,&m); int ok=0; for(int i=0;i<n;i++) for(int j=0;j<m;j++) { char s[5]; scanf("%s",s); if(s[0]!='W'&&s[0]!='B'&&s[0]!='G')

【SAS BASE】ARRAY语句及多变量简写

ARRAY语句: 1 ARRAY array-name (n) $ variable-list; 2 array名 变量个数 变量列表(若为字符型,则需要$) 3 4 ARRAY STORE (4) Macys Penneys Sears Target; 5 DO i=1 to 10; 6 IF STORE(i)=9 THEN song(i)=.; 7 END; 多变量简写 1 /*以相同的字符开头,以连续的数字结尾(两条等价)*/ 2 INPUT cat8 cat9 cat10 cat11 c

【Codeforces 1114C】Trailing Loves (or L&#39;oeufs?)

[链接] 我是链接,点我呀:) [题意] 问你n!的b进制下末尾的0的个数 [题解] 证明:https://blog.csdn.net/qq_40679299/article/details/81167283 这题的话m比较大, 做个质因数分解就ok>_< 算n!有多少个x因子的话 以5为例子 (n=25) 25 20 15 10 5 把他们都除5 5 4 3 2 1 然后再除5 1 所以总共有6个 转换成代码就是 while(n>0){ ans+=n/5; n = n/5; } [代码

【Codeforces 332C】Students&#39; Revenge

Codeforces 332 C 我爱对拍,对拍使我快乐... 题意:有\(n\)个议题,学生们会让议会同意\(p\)个,其中主席会执行\(k\)个, 每一个议题执行后主席会掉\(a_i\)的头发,不执行后议会会增加\(b_i\)的不开心值, 然后主席想让议会的不开心值最小,如果有多重方案就选自己头发掉的最少的: 而学生们想让主席的头发掉的最多,如果有多种方案让议会的不开心值最大. 问让议会同意哪\(p\)个会达到最好的效果. 思路1: 这是我的不对的思路. (虽然没提交 我们首先将所有的数按照

【Codeforces 258B】 Sort the Array

[题目链接] http://codeforces.com/contest/451/problem/B [算法] 模拟 在序列中找到一段单调递增的子序列,将这段序列反转,然后判断序列是否变得单调递增,即可 [代码] #include<bits/stdc++.h> using namespace std; const int MAXN = 1e5 + 10; int i,n,l,r; bool flag; int a[MAXN]; int main() { scanf("%d"

【js实例】Array类型的9个数组方法,Date类型的41个日期方法,Function类型

前文提要:[js实例]js中的5种基本数据类型和9种操作符 Array类型的9个数组方法 Array中有9个数组方法: 1.检测数组 2.转换方法 3.栈方法 4.队列方法 5.冲排序方法6.操作方法 7.位置方法 8.迭代方法 9.归并方法 在实例中介绍,实例如下 /* Array类型 js数组中的每一项可以用来保存任何类型的数据:js数组的大小是可以动态调整的 */ var colors = ["red", "blue", "green"];

【Codeforces 429D】 Tricky Function

[题目链接] http://codeforces.com/problemset/problem/429/D [算法] 令Si = A1 + A2 + ... + Ai(A的前缀和) 则g(i,j) = Sj - Si f(i,j) = (i-j)^2 + (Si - Sj)^2 观察这个式子,我们发现可以用类似于平面最近点对的算法来求解该问题 [代码] #include<bits/stdc++.h> using namespace std; #define MAXN 100010 const