CodeForces 624D Array GCD

传送门: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;
} 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值