题意
一个序列每个数都大于 1 1 1,要使整个数列的最大公约数大于 1 1 1,可以最多删除一个子串,每一个数花费 a a a元,也可以给一个数增添 1 1 1或者减少 1 1 1,每个数最多操作一次.求达成目标最少需要花费的钱数.
题解
当整个数列的
g
c
d
gcd
gcd确定了的时候,可以用
d
p
dp
dp求出最少需要的钱数.
用dp[0][i]
表示前
i
i
i个数中没有被删除的数时的最低需求.
dp[1][i]
表示第
i
i
i个数被删除时的最低需求.
dp[2][i]
表示第
i
i
i个数未被删除并且前面已经有数被删除时的最低需求.
代码如下.
for (int i=1;i<=n;++i) {
if (c[i]%x==0) {
dp[0][i]=dp[0][i-1];
dp[2][i]=min(dp[1][i-1],dp[2][i-1]);
} else if ((c[i]+1)%x==0||(c[i]-1)%x==0) {
dp[0][i]=dp[0][i-1]+b;
dp[2][i]=min(dp[1][i-1],dp[2][i-1])+b;
}
dp[1][i]=min(dp[0][i-1],dp[1][i-1])+a;
}
接下来再考虑有哪些数字可能成为可行的最大公约数.
显然第一个数和最后一个数中必有一个数被保留,并且可能被增添
1
1
1或者减少
1
1
1.这六个数字我们可以暴力分解它们的质因数并对每个数字进行一次上面这样的
d
p
dp
dp.本题迎刃而解.
#include<bits/stdc++.h> //Ithea Myse Valgulious
/*省略*/
using namespace std;
const int yuzu=1e6;
typedef ll fuko[yuzu|10];
fuko c,p={1,1},pr,dp[3];
vector<int> g;
void gpr(int n) {
int i,j;
for (i=2;i<=n;++i) {
if (!p[i]) pr[++*pr]=i;
for (j=1;j<=*pr&&i*pr[j]<=n;++j) {
p[i*pr[j]]=1;
if (i%pr[j]==0) break;
}
}
}
void get(int x) {
for (int i=1;i<=*pr;++i) {
if (x%pr[i]==0)
for (g.push_back(pr[i]);x%pr[i]==0;x/=pr[i]);
}
if (x>1) g.push_back(x);
}
ll cal(int x,int n,int a,int b) {
memset(dp,0x3f,sizeof dp),**dp=0;
for (int i=1;i<=n;++i) {
if (c[i]%x==0) {
dp[0][i]=dp[0][i-1];
dp[2][i]=min(dp[1][i-1],dp[2][i-1]);
} else if ((c[i]+1)%x==0||(c[i]-1)%x==0) {
dp[0][i]=dp[0][i-1]+b;
dp[2][i]=min(dp[1][i-1],dp[2][i-1])+b;
}
dp[1][i]=min(dp[0][i-1],dp[1][i-1])+a;
}
return min(min(dp[0][n],dp[1][n]),dp[2][n]);
}
int main() {
int i,n,a,b;
gpr(yuzu),read(n),read(a),read(b);
for (i=1;i<=n;++i) c[i]=read();
get(c[1]-1),get(c[1]+1),get(c[1]);
get(c[n]-1),get(c[n]+1),get(c[n]);
ll lxy=1e18;
for (auto x:g) lxy=min(lxy,cal(x,n,a,b));
printf("%lld\n",lxy);
}