过年玩了20天 都不会写了
题意:有两种操作,每种只能用一次,第一种对于一段连续区间进行移除(不能全删完),代价是长度*a,第二种是对于一些数进行+1或者-1,使得最后的剩余的最大公约数大于1
思路:由于不能全删完,所以至少会有一个数留着,这个数肯定会是头一个或最后一个,最大公约数肯定是在选中的这个数最后状态中的一个约数,而我们只要先枚举这个数是多少(一共6种),然后枚举他的素因子,用dp顺推即可。
#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
#include<queue>
#include<math.h>
using namespace std;
#define LL long long
#define N 1000010
LL dp[N][3];
int a[N];
LL cost1,cost2;
int prime[N];
int vis[N];
int cnt;
int fac[N];
void init()
{
for(int i=2;i<N;i++)
{
if(!vis[i])prime[cnt++]=i;
for(int j=0;j<cnt&&prime[j]*i<N;j++)
{
vis[prime[j]*i]=1;
if(i%prime[j]==0)break;
}
}
}
LL getAns(int st,int ed,int v)
{
memset(dp,0x3f,sizeof(dp));
//printf("%lld",dp[0][0]);
dp[st-1][0]=0;
for(int i=st;i<=ed;i++)
{
dp[i][1]=min(dp[i-1][0],dp[i-1][1])+cost1;
if(a[i]%v!=0)
{
if((a[i]+1)%v==0||(a[i]-1)%v==0)
{
dp[i][0]=dp[i-1][0]+cost2;
dp[i][2]=min(dp[i-1][1],dp[i-1][2])+cost2;
}
}
else
{
dp[i][0]=dp[i-1][0];
dp[i][2]=min(dp[i-1][1],dp[i-1][2]);
}
}
return min(min(dp[ed][0],dp[ed][1]),dp[ed][2]);
}
int factor(int x)
{
int tot=0;
for(int j=0;j<cnt&&x>=prime[j];j++)
{
if(x%prime[j]==0)
{
fac[tot++]=prime[j];
while(x%prime[j]==0)
x/=prime[j];
}
}
if(x!=1)
fac[tot++]=x;
return tot;
}
int main()
{
init();
int n;
scanf("%d%lld%lld",&n,&cost1,&cost2);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
LL ans=cost1*(LL)n;
for(int i=-1;i<=1;i++)
{
LL cost=i==0?0:cost2;
int siz=factor(a[n]+i);
for(int j=0;j<siz;j++)
ans=min(ans,getAns(1,n-1,fac[j])+cost);
siz=factor(a[1]+i);
for(int j=0;j<siz;j++)
ans=min(ans,getAns(2,n,fac[j])+cost);
}
printf("%lld\n",ans);
return 0;
}