CF1547F Array Stabilization (GCD version) 二分+ST表

Array Stabilization (GCD version)

You are given an array of positive integers a = [ a 0 , a 1 , … , a n − 1 ] a = [a_0, a_1, \dots, a_{n - 1}] a=[a0,a1,,an1] ( n ≥ 2 n \ge 2 n2).

In one step, the array a a a is replaced with another array of length n n n, in which each element is the greatest common divisor (GCD) of two neighboring elements (the element itself and its right neighbor; consider that the right neighbor of the ( n − 1 ) (n - 1) (n1)-th element is the 0 0 0-th element).

Formally speaking, a new array b = [ b 0 , b 1 , … , b n − 1 ] b = [b_0, b_1, \dots, b_{n - 1}] b=[b0,b1,,bn1] is being built from array a = [ a 0 , a 1 , … , a n − 1 ] a = [a_0, a_1, \dots, a_{n - 1}] a=[a0,a1,,an1] such that b i b_i bi = gcd ⁡ ( a i , a ( i + 1 ) m o d    n ) = \gcd(a_i, a_{(i + 1) \mod n}) =gcd(ai,a(i+1)modn), where gcd ⁡ ( x , y ) \gcd(x, y) gcd(x,y) is the greatest common divisor of x x x and y y y, and x m o d    y x \mod y xmody is the remainder of x x x dividing by y y y. In one step the array b b b is built and then the array a a a is replaced with b b b (that is, the assignment a a a := b b b is taking place).

For example, if a = [ 16 , 24 , 10 , 5 ] a = [16, 24, 10, 5] a=[16,24,10,5] then b = [ gcd ⁡ ( 16 , 24 ) b = [\gcd(16, 24) b=[gcd(16,24), gcd ⁡ ( 24 , 10 ) \gcd(24, 10) gcd(24,10), gcd ⁡ ( 10 , 5 ) \gcd(10, 5) gcd(10,5), gcd ⁡ ( 5 , 16 ) ] \gcd(5, 16)] gcd(5,16)] = [ 8 , 2 , 5 , 1 ] = [8, 2, 5, 1] =[8,2,5,1]. Thus, after one step the array a = [ 16 , 24 , 10 , 5 ] a = [16, 24, 10, 5] a=[16,24,10,5] will be equal to [ 8 , 2 , 5 , 1 ] [8, 2, 5, 1] [8,2,5,1].

For a given array a a a, find the minimum number of steps after which all values a i a_i ai become equal (that is, a 0 = a 1 = ⋯ = a n − 1 a_0 = a_1 = \dots = a_{n - 1} a0=a1==an1). If the original array a a a consists of identical elements then consider the number of steps is equal to 0 0 0.

Input

The first line contains an integer t t t ( 1 ≤ t ≤ 1 0 4 1 \le t \le 10^4 1t104). Then t t t test cases follow.

Each test case contains two lines. The first line contains an integer n n n ( 2 ≤ n ≤ 2 ⋅ 1 0 5 2 \le n \le 2 \cdot 10^5 2n2105) — length of the sequence a a a. The second line contains n n n integers a 0 , a 1 , … , a n − 1 a_0, a_1, \dots, a_{n - 1} a0,a1,,an1 ( 1 ≤ a i ≤ 1 0 6 1 \le a_i \le 10^6 1ai106).

It is guaranteed that the sum of n n n over all test cases doesn’t exceed 2 ⋅ 1 0 5 2 \cdot 10^5 2105.

Output

Print t t t numbers — answers for each test case.

Example

input

5
4
16 24 10 5
4
42 42 42 42
3
4 6 4
5
1 2 3 4 5
6
9 9 27 9 9 63

output

3
0
2
1
1

题目翻译

给你一个由正整数 a = [ a 0 , a 1 , … , a n − 1 ] a = [a_0, a_1, \dots, a_{n - 1}] a=[a0,a1,,an1] ( n ≥ 2 n \ge 2 n2 ) 组成的数组。

在一个步骤中,数组 a a a 被另一个长度为 n n n 的数组取代,其中每个元素都是两个相邻元素(元素本身及其右邻;考虑到 ( n − 1 ) (n - 1) (n1) /th 元素的右邻是 0 0 0 /th 元素)的 最大公约数 (GCD)

从形式上来说,一个新的数组 b = [ b 0 , b 1 , … , b n − 1 ] b = [b_0, b_1, \dots, b_{n - 1}] b=[b0,b1,,bn1] 是由数组 a = [ a 0 , a 1 , … , a n − 1 ] a = [a_0, a_1, \dots, a_{n - 1}] a=[a0,a1,,an1] 建立的,其中 b i b_i bi = gcd ⁡ ( a i , a ( i + 1 ) m o d    n ) = \gcd(a_i, a_{(i + 1) \mod n}) =gcd(ai,a(i+1)modn) gcd ⁡ ( x , y ) \gcd(x, y) gcd(x,y) x x x y y y x m o d    y x \mod y xmody = gcd ⁡ ( a i , a ( i + 1 ) m o d    n ) = \gcd(a_i, a_{(i + 1) \mod n}) =gcd(ai,a(i+1)modn) ,其中 gcd ⁡ ( x , y ) \gcd(x, y) gcd(x,y) x x x y y y 的最大公约数,而 x m o d    y x \mod y xmody x x x 除以 y y y 的余数。一步建立数组 b b b ,然后用 b b b 替换数组 a a a (即赋值 a a a 除以 b b b 的余数)。(即赋值 a a a := b b b )。

例如,如果是 a = [ 16 , 24 , 10 , 5 ] a = [16, 24, 10, 5] a=[16,24,10,5] 则是 b = [ gcd ⁡ ( 16 , 24 ) b = [\gcd(16, 24) b=[gcd(16,24) , gcd ⁡ ( 24 , 10 ) \gcd(24, 10) gcd(24,10) , gcd ⁡ ( 10 , 5 ) \gcd(10, 5) gcd(10,5) , gcd ⁡ ( 5 , 16 ) ] \gcd(5, 16)] gcd(5,16)] . = [ 8 , 2 , 5 , 1 ] = [8, 2, 5, 1] =[8,2,5,1] .这样,经过一步后,数组 a = [ 16 , 24 , 10 , 5 ] a = [16, 24, 10, 5] a=[16,24,10,5] 将等于 [ 8 , 2 , 5 , 1 ] [8, 2, 5, 1] [8,2,5,1]

对于给定的数组 a a a ,求所有值 a i a_i ai 都相等(即 a 0 = a 1 = ⋯ = a n − 1 a_0 = a_1 = \dots = a_{n - 1} a0=a1==an1 )的最小步数。如果原数组 a a a 由完全相同的元素组成,则考虑步数等于 0 0 0

输入格式

第一行包含一个整数 t t t ( 1 ≤ t ≤ 1 0 4 1 \le t \le 10^4 1t104 )。然后是 t t t 个测试用例。

每个测试用例包含两行。第一行包含一个整数 n n n ( 2 ≤ n ≤ 2 ⋅ 1 0 5 2 \le n \le 2 \cdot 10^5 2n2105 ) - 序列长度 a a a 。第二行包含 n n n 个整数 a 0 , a 1 , … , a n − 1 a_0, a_1, \dots, a_{n - 1} a0,a1,,an1 ( 1 ≤ a i ≤ 1 0 6 1 \le a_i \le 10^6 1ai106 )。( 1 ≤ a i ≤ 1 0 6 1 \le a_i \le 10^6 1ai106 ).

保证所有测试用例的 n n n 之和不超过 2 ⋅ 1 0 5 2 \cdot 10^5 2105

输出格式

打印 t t t 个数字 - 每个测试用例的答案。

解题思路

前置知识

ST表

  • 利用二进制进行跳跃,用于优化时间复杂度(预处理 n × log ⁡ n n\times \log n n×logn) 查询 O ( 1 ) O(1) O(1)
  • 可处理RMQ和GCD问题(重复计算部分无所谓)

正文

由题意可知,经过 k k k 次变化后 :
a 1 = gcd ⁡ ( a 1 , a 2 … … a k + 1 m o d    n ) a_1=\gcd(a_1,a_2……a_{k+1 \mod n}) a1=gcd(a1,a2……ak+1modn)
a 2 = gcd ⁡ ( a 2 , a 3 … … a k + 2 m o d    n ) a_2=\gcd(a_2,a_3……a_{k+2 \mod n}) a2=gcd(a2,a3……ak+2modn)
… \dots
a i = gcd ⁡ ( a i , a i + 1 … … a k + i m o d    n ) a_i=\gcd(a_i,a_{i+1}……a_{k+i \mod n}) ai=gcd(ai,ai+1……ak+imodn)
所以可以将问题转化为:求一个最小的 k k k 使得 a a a 中每一个长度大于 k k k 的区间的 gcd ⁡ \gcd gcd gcd ⁡ ( a 1 , a 2 , . . . , a n ) \gcd(a_1,a_2,...,a_n) gcd(a1,a2,...,an)

可以发现, k k k 具有单调性。因为若 k k k 满足题意,则比 k k k 大也必定满足题意,因为 gcd ⁡ ( a , a ) = a \gcd(a,a)=a gcd(a,a)=a。所以我们考虑二分答案:每次二分出一个 k k k,求出 a a a 中每个长度为 k k k 的序列的 gcd ⁡ \gcd gcd,比较是否相等。

现在我们就需要一个快速的算法求 a 中一段子序列的 gcd,所以考虑 ST表。因为 gcd ⁡ ( gcd ⁡ ( a , b ) , gcd ⁡ ( a , c ) ) = gcd ⁡ ( a , b , c ) \gcd(\gcd(a,b),\gcd(a,c))=\gcd(a,b,c) gcd(gcd(a,b),gcd(a,c))=gcd(a,b,c)

时间复杂度: O ( n × log ⁡ 2 n ) O(n\times \log^{2} n) O(n×log2n)

AC Code

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int Maxn = 2e5 + 10;
int n, a[Maxn];
int lg2[Maxn], ST[Maxn][40];
int ans;

inline void init();

inline void solve();
inline int Binary_search(int n);
inline bool check(int k);
inline int ask(int l, int r);

inline void work() {
	init();
	int T;
	cin >> T;
	while (T--) {
		solve();
	}
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	work();
	return 0;
}

inline void init() {
	for (int i = 2; i < Maxn; i++) {
		lg2[i] = lg2[i >> 1] + 1;
	}
}

inline void solve() {
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		ST[i][0] = a[i];
	}
	for (int j = 1; j <= lg2[n]; j++) {
		for (int i = 1; i <= (n) - (1 << j) + 1; i++) {
			ST[i][j] = __gcd(ST[i][j - 1], ST[i + (1 << j - 1)][j - 1]);
		}
	}
	ans = Binary_search(n);
	cout << ans << endl;
}
inline int Binary_search(int n) {
	int l = 1, r = n, mid;
	while (l < r) {
		mid = l + r >> 1;
		if (check(mid)) {
			r = mid;
		} else {
			l = mid + 1;
		}
	}
	return l - 1;
}
inline int ask(int l, int r) {
	if (l <= n && r > n) {
		return __gcd(ask(l, n), ask(1, r - n));
	}
	int k = lg2[r - l + 1];
	return __gcd(ST[l][k], ST[r - (1 << k) + 1][k]);
}
inline bool check(int k) {
	int t = ask(1, 1 + k - 1);
	for (int i = 2; i <= n; i++) {
		if (ask(i, i + k - 1) != t) {
			return 0;
		}
	}
	return 1;
}

汗流浃背了吧。

  • 32
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值