传送门:
http://codeforces.com/contest/623/problem/B
题意:
有n个数,你可以花费i*a去删除长度i的线段,也可以花费B去让一个数+-1,但是删除操作只能进行一次,+-1对一个数也只能操作一次
并且删除操作不能删除所有的数
问你最小花费多少,可以使得剩下的数的gcd不等于1
题解:
很显然,因为不能删除完,所以必然第一个数和最后一个数会剩下来
所以我们暴力枚举第一个数和最后一个数的质因子就好了
然后开始跑dp
dp[i][0,1,2] 分别表示子串未开始,开始了,取完了的状态下达到目标所需要的最低花费,然后dp就可以了!!
code:
#include <cstdio>
#include <algorithm>
#include <set>
using namespace std;
set<int> fac;
void factor(int x)
{
for (int i = 2; i * i <= x; ++i)
{
if (x % i == 0)
{
fac.insert(i);
while (x % i == 0)
x /= i;
}
}
if (x > 1)
fac.insert(x);
}
int n, a, b;
int p[1000000];
long long dp[1000001][3];
long long check(int v)
{
dp[0][0] = 0;
dp[0][1] = dp[0][2] = 1e18;
for (int i = 0; i < n; ++i)
{
dp[i + 1][0] = dp[i + 1][1] = dp[i + 1][2] = 1e18;
if (p[i] % v == 0)
{
dp[i + 1][0] = dp[i][0];
dp[i + 1][2] = min(dp[i][1], dp[i][2]);
}
else if ((p[i] + 1) % v == 0 || (p[i] - 1) % v == 0)
{
dp[i + 1][0] = dp[i][0] + b;
dp[i + 1][2] = min(dp[i][1], dp[i][2]) + b;
}
dp[i + 1][1] = min(dp[i][0], dp[i][1]) + a;
}
return min({dp[n][0], dp[n][1], dp[n][2]});
}
int main()
{
scanf("%d %d %d", &n, &a, &b);
for (int i = 0; i < n; ++i)
scanf("%d", &p[i]);
for (int u: {-1, 0, 1})
for (int v: {p[0], p[n - 1]})
factor(u + v);
long long ans = 1e18;
for (int v: fac)
ans = min(ans, check(v));
printf("%I64d\n", ans);
return 0;
}
附上另一种写法(原理一样,只不过就用了3个状态去表示):
并且体会一下vector的去重方法
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
const LL INF=10000000000000000LL;
const int N=1000000+10;
vector<int>V;
int n,x[N];
LL a,b;
void MakePrime(int x)
{
int Max=int(sqrt((double)x));
for (int i=2;i<=Max;i++){
if (x%i==0){
V.push_back(i);
while (x%i==0)x/=i;
}
}
if (x>1)V.push_back(x);
}
LL Solve(int p)
{
LL s1=0,s2=0,s3=0;
for (int i=1;i<=n;i++){
LL Now=INF;
if (x[i]%p==0)Now=0;
else if ((x[i]-1)%p==0 || (x[i]+1)%p==0)Now=b;
s1=min(s1+Now,INF);s2=min(s1,s2+a);s3=min(s2,s3+Now);
}
return s3;
}
int main()
{
cin>>n>>a>>b;
for (int i=1;i<=n;i++)scanf("%d",&x[i]);
for (int i=-1;i<=1;i++){
MakePrime(x[1]+i);
MakePrime(x[n]+i);
}
sort(V.begin(),V.end());
V.erase(unique(V.begin(),V.end()),V.end());
LL Ans=INF;
for (int i=0;i<V.size();i++){
Ans=min(Ans,Solve(V[i]));
}
cout<<Ans<<endl;
return 0;
}