1
+ import java .util .ArrayList ;
2
+ import java .util .Comparator ;
3
+ import java .util .List ;
4
+
5
+ public class Solution3605 {
6
+ // 200ms
7
+ static class V1 {
8
+ private int [][] st ;
9
+ private int [] log ;
10
+ private int n ;
11
+
12
+ public int minStable (int [] nums , int maxC ) {
13
+ n = nums .length ;
14
+ if (n == 0 ) return 0 ;
15
+ buildSparseTable (nums );
16
+ precomputeLogs ();
17
+
18
+ int low = 0 , high = n ;
19
+ while (low <= high ) {
20
+ int mid = (low + high ) >> 1 ;
21
+ if (check (mid , maxC , nums )) {
22
+ high = mid - 1 ;
23
+ } else {
24
+ low = mid + 1 ;
25
+ }
26
+ }
27
+ return low ;
28
+ }
29
+
30
+ private boolean check (int k , int maxC , int [] nums ) {
31
+ if (k == 0 ) {
32
+ int count = 0 ;
33
+ for (int num : nums ) {
34
+ if (num >= 2 ) count ++;
35
+ }
36
+ return count <= maxC ;
37
+ }
38
+ int L = k + 1 ;
39
+ if (L > n ) {
40
+ return true ;
41
+ }
42
+ List <int []> intervals = new ArrayList <>();
43
+ for (int i = 0 ; i <= n - L ; i ++) {
44
+ int g = queryGCD (i , i + L - 1 );
45
+ if (g >= 2 ) {
46
+ intervals .add (new int []{i , i + L - 1 });
47
+ }
48
+ }
49
+ if (intervals .isEmpty ()) {
50
+ return true ;
51
+ }
52
+ intervals .sort (Comparator .comparingInt (a -> a [1 ]));
53
+ int points = 0 ;
54
+ int last = -1 ;
55
+ for (int [] interval : intervals ) {
56
+ int left = interval [0 ];
57
+ int right = interval [1 ];
58
+ if (left > last ) {
59
+ points ++;
60
+ last = right ;
61
+ }
62
+ }
63
+ return points <= maxC ;
64
+ }
65
+
66
+ private void buildSparseTable (int [] nums ) {
67
+ int k = (int ) (Math .log (n ) / Math .log (2 )) + 1 ;
68
+ st = new int [k ][n ];
69
+ System .arraycopy (nums , 0 , st [0 ], 0 , n );
70
+ for (int j = 1 ; j < k ; j ++) {
71
+ int step = 1 << (j - 1 );
72
+ for (int i = 0 ; i + (1 << j ) <= n ; i ++) {
73
+ st [j ][i ] = gcd (st [j - 1 ][i ], st [j - 1 ][i + step ]);
74
+ }
75
+ }
76
+ }
77
+
78
+ private void precomputeLogs () {
79
+ log = new int [n + 1 ];
80
+ log [1 ] = 0 ;
81
+ for (int i = 2 ; i <= n ; i ++) {
82
+ log [i ] = log [i / 2 ] + 1 ;
83
+ }
84
+ }
85
+
86
+ private int queryGCD (int l , int r ) {
87
+ int len = r - l + 1 ;
88
+ int j = log [len ];
89
+ return gcd (st [j ][l ], st [j ][r - (1 << j ) + 1 ]);
90
+ }
91
+
92
+ private int gcd (int a , int b ) {
93
+ if (b == 0 ) return a ;
94
+ return gcd (b , a % b );
95
+ }
96
+ }
97
+
98
+ // 144ms
99
+ static class V2 {
100
+ public int minStable (int [] nums , int maxC ) {
101
+ int left = 0 ;
102
+ int right = nums .length / (maxC + 1 );
103
+ while (left < right ) {
104
+ int mid = left + (right - left ) / 2 ;
105
+ // 边界二分 F, F,..., F, [T, T,..., T]
106
+ // ----------------------^
107
+ if (check (nums , maxC , mid )) {
108
+ right = mid ;
109
+ } else {
110
+ left = mid + 1 ;
111
+ }
112
+ }
113
+ return left ;
114
+ }
115
+
116
+ private boolean check (int [] nums , int maxC , int upper ) {
117
+ List <int []> intervals = new ArrayList <>(); // 每个元素是 (子数组 GCD,最小左端点)
118
+ for (int i = 0 ; i < nums .length ; i ++) {
119
+ int x = nums [i ];
120
+
121
+ // 计算以 i 为右端点的子数组 GCD
122
+ for (int [] interval : intervals ) {
123
+ interval [0 ] = getGCD (interval [0 ], x );
124
+ }
125
+ // nums[i] 单独一个数作为子数组
126
+ intervals .add (new int []{x , i });
127
+
128
+ // 去重(合并 GCD 相同的区间)
129
+ int idx = 1 ;
130
+ for (int j = 1 ; j < intervals .size (); j ++) {
131
+ if (intervals .get (j )[0 ] != intervals .get (j - 1 )[0 ]) {
132
+ intervals .set (idx , intervals .get (j ));
133
+ idx ++;
134
+ }
135
+ }
136
+ intervals .subList (idx , intervals .size ()).clear ();
137
+
138
+ // intervals 的性质:越靠左,GCD 越小
139
+
140
+ // 我们只关心 GCD >= 2 的子数组
141
+ if (intervals .get (0 )[0 ] == 1 ) {
142
+ intervals .remove (0 );
143
+ }
144
+
145
+ // intervals[0] 的 GCD >= 2 且最长,取其区间左端点作为子数组的最小左端点
146
+ if (!intervals .isEmpty () && i - intervals .get (0 )[1 ] + 1 > upper ) {
147
+ if (maxC == 0 ) {
148
+ return false ;
149
+ }
150
+ maxC --;
151
+ intervals .clear (); // 修改后 GCD 均为 1,直接清空
152
+ }
153
+ }
154
+ return true ;
155
+ }
156
+
157
+ private int getGCD (int num1 , int num2 ) {
158
+ return num1 == 0 ? num2 : getGCD (num2 % num1 , num1 );
159
+ }
160
+ }
161
+ }
162
+ /*
163
+ 3605. 数组的最小稳定性因子
164
+ https://leetcode.cn/problems/minimum-stability-factor-of-array/description/
165
+
166
+ 第 160 场双周赛 T4。
167
+
168
+ 给你一个整数数组 nums 和一个整数 maxC。
169
+ 如果一个 子数组 的所有元素的最大公因数(简称 HCF) 大于或等于 2,则称该子数组是稳定的。
170
+ 一个数组的 稳定性因子 定义为其 最长 稳定子数组的长度。
171
+ 你 最多 可以修改数组中的 maxC 个元素为任意整数。
172
+ 在最多 maxC 次修改后,返回数组的 最小 可能稳定性因子。如果没有稳定的子数组,则返回 0。
173
+ 注意:
174
+ - 子数组 是数组中连续的元素序列。
175
+ - 数组的 最大公因数(HCF)是能同时整除数组中所有元素的最大整数。
176
+ - 如果长度为 1 的 子数组 中唯一元素大于等于 2,那么它是稳定的,因为 HCF([x]) = x。
177
+ 提示:
178
+ 1 <= n == nums.length <= 10^5
179
+ 1 <= nums[i] <= 10^9
180
+ 0 <= maxC <= n
181
+
182
+ 二分答案 + ST 表。
183
+ 时间复杂度 O(nlogU)
184
+ 二分答案 + logTrick。
185
+ */
0 commit comments