CF623B Array GCD

CF623B Array GCD

题目大意:

给一个长为\(n\)的序列\(v\).有两操作如下:
一:删除一段长为\(m(m<n)\)的连续子序列,费用为\(m*a\).操作一只能执行一次.
二:给一个数\(+1/-1\),费用为\(b\).每个数只能执行一次操作二.

求使所有数最大公约数大于\(1\)的最小代价.

题解:

由于序列不能全部删完,所以最后必定会留下第一个数或最后一个数,而这两个数又能够加减\(1\)或保持不变,所以共有六种情况.
我们枚举这六种情况,然后枚举质因子\(x\)进行\(\mathcal{DP}\).
我们先预处理出每个数\(v\)的代价\(c\).

  • \(v%x==0\)\(c=0\).
  • \((v+1)%x==0\)\((v-1)%x==0\)\(c=b\)
  • 其它情况\(c=inf\)
    \(dp\)状态:\(f[i][0/1/2]\)表示到第\(i\)个位置未删/在删/删完数的最小代价.则有转移方程如下:

\[f[i][0]=f[i-1][0]+c[i] \]

\[f[i][1]=min(f[i-1][0], f[i-1][1])+a \]

\[f[i][2]=min(f[i-1][1], f[i-1][2])+c[i] \]

然后据此转移即可.


const ll inf=1000000000000000LL, N=1e6+6;

ll n, a, b;
ll v[N];
ll res=inf;

ll c[N];
ll f[N][3];
inline void calc(ll x){
	for (R ll i=1; i<=n; i++){
		if (v[i]%x==0) c[i]=0;
		else if (v[i]%x==1 || v[i]%x==x-1) c[i]=b;
		else c[i]=inf;
	}
	for (R ll i=1; i<=n; i++){
		f[i][0]=min(inf, f[i-1][0]+c[i]);
		f[i][1]=min(inf, min(f[i-1][0], f[i-1][1])+a);
		f[i][2]=min(inf, min(f[i-1][1], f[i-1][2])+c[i]);
	}
	chkmin(res, f[n][0]);
	chkmin(res, f[n][1]);
	chkmin(res, f[n][2]);
}

inline void solve(ll x){
	for (R ll i=2; i<=100000; i++){
		if (x%i==0){
			calc(i);
			while (x%i==0) x/=i;
		}
	}
	if (x!=1) calc(x);
}
int main(){
	read(n); read(a); read(b);
	for (R ll i=1; i<=n; i++) read(v[i]);
	for (R ll i=-1; i<=1; i++) solve(i+v[1]), solve(i+v[n]);
	writeln(res);
}
posted @ 2020-12-03 20:09  月落乌啼算钱  阅读(99)  评论(0编辑  收藏  举报