传送门

Solution

首先考虑数组内已经有 11 的情况。设此时有 n1n_111,则答案为 nn1n-n_1

再考虑数组内没有 11 的情况。要想让整个数组变成 11,首先要先在某一段连续的区间内进行 gcd\gcd 操作,生成第一个 11。然后通过 n1n-1gcd\gcd,把数组的其余元素变成 11。设生成第一个 11 的连续区间长度为 lenlen,本题答案即为

(len1)+(n1)(len - 1) + (n - 1)

注意到每次操作只能改变一个元素的值,所以这个答案的正确性是显然的。

问题转化为如何找到能生成第一个 11,即区间的 gcd\gcd11 的最短区间。如果用朴素的枚举,复杂度为 Θ(n2)\Theta(n^2)。这里考虑用二分答案 + 线段树优化。线段树 O(logn)O(\log n) 查询区间的 gcd\gcd。注意到左端点确定时,区间的 gcd\gcd 随右端点的增大而递减。因此枚举区间的左端点,二分答案找出符合要求的最小右端点即可。

复杂度 Θ(nlogn)\Theta(n\log n)

code

#include <bits/stdc++.h>
#define N 100010
#define ls k<<1
#define rs k<<1|1
using namespace std;
typedef long long ll;

template <typename T> inline void read(T &x)
{
	x = 0; T f = 1; char ch = getchar();
	while (!isdigit(ch)) {if (ch == '-') f = -f; ch = getchar();}
	while (isdigit(ch)) {x = x * 10 + ch - 48; ch = getchar();}
	x *= f;
}

int n, a[N], ans = 1e9, cnt; 
struct Tree {
	int l, r, val;
} T[N << 2];

void build(int k, int l, int r)
{
	T[k].l = l; T[k].r = r;
	if (l == r) {
		T[k].val = a[l];
		return;
	}
	int mid = l + r >> 1;
	build(ls, l, mid);
	build(rs, mid + 1, r);
	T[k].val = __gcd(T[ls].val, T[rs].val);
}

int query(int k, int x, int y)
{
	if (x <= T[k].l && T[k].r <= y)
		return T[k].val;
	int mid = T[k].l + T[k].r >> 1, resl = 0, resr = 0;
	if (x <= mid)  resl = query(ls, x, y);
	if (mid < y) resr = query(rs, x, y);
	return __gcd(resl, resr);
}

int main()
{
	freopen("a.txt", "r", stdin);
	read(n);
	for (int i = 1; i <= n; i++) {
		read(a[i]);
		cnt += (a[i] == 1);
	}
	if (cnt) {
		printf("%d", n - cnt);
		return 0;
	}
	build(1, 1, n);
	for (int i = 1; i <= n; i++) {
		int le = i, ri = n, mid, res = n + 1;
		while (le <= ri) {
			mid = le + ri >> 1;
			if (query(1, i, mid) == 1) {
				res = mid;
				ri = mid - 1;
			} else le = mid + 1;
		}
		if (res != n + 1)
			ans = min(ans, res - i + n - 1);
	}
	if (ans == 1e9) printf("-1");
	else printf("%d", ans);
	return 0;
}