传送门:http://codeforces.com/problemset/problem/624/D
题目大意:给一个序列,有两种操作
1,删掉某一个连续区间,代价为区间长度*a
2,把某些数+1或-1,代价为更改的数量*b
对于1操作只能进行一次,2操作可多次,并且不能把整个序列删除
使得序列的最大公约数大于1,求最小的代价
因为不能把整个序列删除,所以s[1]与s[n]总会有一个在序列中,所以最后的最大公约数一定是s[1],s[1]+1,s[1]-1,s[n],s[n]+1,s[n]-1的公约数中的一个
而又因为质因数一定更小,所以只需搜索这些数的质因数即可,所以判断所有的数,是不用处理,还是要修改,还是必须删掉
而更优的代价只能在某些修改的数改为删掉可能会更优,在求答案是用尺取法(又是尺取。。。)
如果当前的区间,删掉已经不如修改优了,就令r=l+1重新开始找区间
代码如下:
#include<cstdio>
#include<iostream>
using namespace std;
int tot=0;
long long c[100000];
long long s[1000005];
long long db[1000005];
long long da[1000005];
int n;
long long a,b;
void insert(long long x)
{
for (int i=1;i<=tot;i++)
{
if (c[i]==x)
{
return;
}
}
tot++;
c[tot]=x;
}
long long judge(long long x,long long y)
{
if (x%y==0)
{
return 0;
}
if ((x+1)%y==0 || (x-1)%y==0)
{
return b;
}
return 1e17;
}
long long get()
{
long long nowans=1e18;
long long now=0;
int l,r;
for (int i=1;i<=tot;i++)
{
da[0]=db[0]=0;
for (int j=1;j<=n;j++)
{
da[j]=da[j-1];
db[j]=db[j-1];
now=judge(s[j],c[i]);
if (now==b)
{
db[j]++;
}
if (now==1e17)
{
da[j]++;
}
}
l=1;
while (da[l]==0 && db[l]==0 && l<=n)
{
l++;
}
r=l-1;
if (l==n+1)
{
return 0;
}
if (da[n]==0)
{
nowans=min(nowans,b*db[n]);
}
long long ac=0;
long long bc=0;
long long last=0;
while (r<n)
{
ac+=a;
r++;
if (bc<1e17)
{
bc+=judge(s[r],c[i]);
}
if (bc<=ac)
{
l=r+1;
last+=bc;
ac=bc=0;
}
else
{
if (da[n]==da[r])
{
nowans=min(nowans,last+ac+b*(db[n]-db[r]));
}
}
}
}
return nowans;
}
int main()
{
scanf("%d%I64d%I64d",&n,&a,&b);
for (int i=1;i<=n;i++)
{
scanf("%I64d",&s[i]);
}
long long now;
for (int i=-1;i<=1;i++)
{
now=s[1]+i;
for (int j=2;j*j<=now;j++)
{
if (now%j==0)
{
insert(j);
while (now%j==0)
{
now/=j;
}
}
}
if (now>1)
{
insert(now);
}
now=s[n]+i;
for (int j=2;j*j<=now;j++)
{
if (now%j==0)
{
insert(j);
while (now%j==0)
{
now/=j;
}
}
}
if (now>1)
{
insert(now);
}
}
printf("%I64d\n",get());
return 0;
}