题意:给定一个包含n n n a a a i i i j j j a i a_i a i  gcd  ( a i , a j ) \gcd(a_i, a_j) g cd( a i  , a j  ) 
所有元素最终必然会相等,且等于整个初始数组的 GCD。问题的核心就变成了如何求得最小的 GCD。
方法一  :看到 5000 考虑O ( n 2 ) \mathcal O(n^{2}) O ( n 2 ) d p i , x dp_{i,x} d p i , x  i i i x x x O ( n V log  V ) \mathcal O(nV\log V) O ( n V log  V ) V = 5000 V=5000 V = 5 0 0 0 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #include  <bits/stdc++.h>  using  namespace  std;using  uint = unsigned ;using  ll = long  long ;using  ull = unsigned  long  long ;using  ulll = unsigned  __int128;constexpr  ll inf = 0x3f3f3f3f3f3f3f3f ;void  solve ()      int  n;     cin >> n;     vector<int > a (n)  ;     int  d = 0 ;     for  (int  i = 0 ; i < n; i++) {         cin >> a[i];          d = gcd (d, a[i]);      }     int  cnt = 0 ;     int  has_one = 0 ;     for  (int  i = 0 ; i < n; i++) {         cnt += a[i] != d;         if  (a[i] == d) has_one = 1 ;     }     const  int  N = 5001 ;     vector<int > dp (N, inf)  ;     for  (int  i = 0 ; i < n; i++) {         auto  ndp = dp;         ndp[a[i]] = 0 ;         for  (int  j = 1 ; j < N; j++) {             ndp[gcd (j, a[i])] = min (ndp[gcd (j, a[i])], dp[j] + 1 );         }          dp = ndp;     }     cout << (dp[d] + cnt - 1  + has_one) << endl; } int  main ()      cin.tie (nullptr )->sync_with_stdio (false );     for  (int  t = (cin >> t, t); t--; )         solve ();     return  0 ; } 
题意:给定一个包含n n n a a a i i i j j j a i a_i a i  gcd  ( a i , a j ) \gcd(a_i, a_j) g cd( a i  , a j  ) 
官方题解给出了本题的 Bonus 版本:Try to solven , a i ≤ 2 × 1 0 5 n, a_i \leq 2 \times 10^5 n , a i  ≤ 2 × 1 0 5 V V V O ( V log  V ) \mathcal{O}(V\log V) O ( V log  V ) 
所有元素最终必然会相等,且等于整个初始数组的 GCD。问题的核心就变成了如何求得最小的 GCD。
考虑到求得最小的 GCD 需要的数不会很多,事实上最大只会是ω ( V ) = 8 \omega(V)=8 ω ( V ) = 8 V V V 
我们采用迭代的方式求解。迭代t t t 恰好选出t t t   。循环ω ( V ) \omega(V) ω ( V ) O ( V log  V ) \mathcal{O}(V\log V) O ( V log  V ) 
设
f [ i ] f[i] f [ i ] i i i g [ t ] [ i ] g[t][i] g [ t ] [ i ] t t t i i i f f f g [ t ] g[t] g [ t ] g [ t ] [ i ] g[t][i] g [ t ] [ i ] f f f x x x g [ t − 1 ] g[t-1] g [ t − 1 ] y y y gcd  ( x , y ) = i \gcd(x, y)=i g cd( x , y ) = i x x x y y y i i i i i i O ( V log  3 V ) \mathcal{O}(V\log^{3} V) O ( V log  3 V ) 
总的复杂度O ( V log  3 V ω ( V ) ) \mathcal{O}(V\log^{3} V\omega(V)) O ( V log  3 V ω ( V ) ) 
进一步优化,引入辅助数组
h [ t ] [ i ] h[t][i] h [ t ] [ i ] f f f x x x g [ t − 1 ] g[t-1] g [ t − 1 ] y y y gcd  ( x , y ) \gcd(x, y) g cd( x , y ) i i i x x x y y y i i i O ( V log  V ) \mathcal{O}(V\log V) O ( V log  V ) h h h 显然h [ t ] [ n ] = ∑ k g [ t ] [ n k ] \displaystyle h[t][n]=\sum_{k}g[t][nk] h [ t ] [ n ] = k ∑  g [ t ] [ n k ] h [ t ] h[t] h [ t ] g [ t ] g[t] g [ t ] 
g [ t ] [ n ] = ∑ k μ [ k ] ⋅ h [ t ] [ n k ] \displaystyle g[t][n] = \sum_{k}\mu[k] \cdot h[t][n k] g [ t ] [ n ] = k ∑  μ [ k ] ⋅ h [ t ] [ n k ] 
总的复杂度O ( V log  V ⋅ ω ( V ) ) \mathcal{O}(V\log V \cdot\omega(V)) O ( V log  V ⋅ ω ( V ) ) 
注:莫比乌斯反演
f [ n ] = ∑ d ∣ n g [ d ]    ⟺    g [ n ] = f [ n ] ∗ μ [ n ] = ∑ d ∣ n μ [ d ] ⋅ f [ n d ] f [ n ] = ∑ d = 1 ∞ g [ n d ]    ⟺    g [ n ] = ∑ d = 1 ∞ μ [ d ] ⋅ f [ n d ] \begin{aligned} f[n]=\displaystyle \sum_{d \mid n} g[d] &\quad\iff\quad g[n]=f[n]\ast\mu[n]=\sum_{d \mid n} \mu[d]\cdot f\Big[\frac{n}{d}\Big] \\ f[n] = \sum_{d=1}^{\infty} g[n d] &\quad\iff\quad g[n] = \sum_{d=1}^{\infty} \mu[d] \cdot f[n d] \end{aligned} f [ n ] = d ∣ n ∑  g [ d ] f [ n ] = d = 1 ∑ ∞  g [ n d ]  ⟺ g [ n ] = f [ n ] ∗ μ [ n ] = d ∣ n ∑  μ [ d ] ⋅ f [ d n  ] ⟺ g [ n ] = d = 1 ∑ ∞  μ [ d ] ⋅ f [ n d ]  
其中莫比乌斯函数μ [ n ] = { 1 , n = 1 0 , ∃ d > 1 , d 2 ∣ n ( − 1 ) ω ( n ) , otherwise \mu[n] = \begin{cases} 1, & n = 1 \\ 0, & \exists d > 1, d^{2} \mid n \\ (-1)^{\omega(n)}, & \text{otherwise}\end{cases} μ [ n ] = ⎩ ⎪ ⎪ ⎨ ⎪ ⎪ ⎧  1 , 0 , ( − 1 ) ω ( n ) ,  n = 1 ∃ d > 1 , d 2 ∣ n otherwise  
核心代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 vector<int > f (V)  ;       vector<int > g (V)  ;       for  (auto  x : a) {	f[x] = 1 ; 	g[x] = 1 ; } for  (int  t = 1 ; ; t++) {	vector<int > h (V)  ; 	for  (int  i = 1 ; i < V; i++) { 		int  sumf = 0 ; 		int  sumg = 0 ; 		for  (int  j = i; j < V; j += i) { 			sumf += f[j]; 			sumg += g[j]; 		} 		h[i] = sumf * sumg; 	} 	for  (int  i = 1 ; i < V; i++) { 		g[i] = 0 ; 		for  (int  k = 1 ; i * k < V; k++) { 			g[i] += mu[k] * h[i * k]; 		} 	} 	if  (g[d] > 0 ) { 		cout << (t + n - 1 ) << endl; 		return ; 	} } 
Zeta - Möbius Transform 用于计算偏序集上一个元素的所有上级(或下级)元素的某个函数值的总和。在这里我们将其应用到「倍数——因子」这个上下级关系,即
X [ n ] = ∑ d = 1 ∞ x [ n d ] X[n] = \sum_{d=1}^{\infty} x[nd] X [ n ] = d = 1 ∑ ∞  x [ n d ] 
代码里算 sumf 和 sumg 就是在求 Mobius 变换。
上面讲解中的h [ t ] h[t] h [ t ] f f f g [ t − 1 ] g[t-1] g [ t − 1 ] h [ t ] [ k ] = F [ k ] ⋅ G [ t ] [ k ] h[t][k]=F[k]\cdot G[t][k] h [ t ] [ k ] = F [ k ] ⋅ G [ t ] [ k ] h [ t ] h[t] h [ t ] g [ t ] g[t] g [ t ] 
从这个角度看,没有必要每次循环都先正变换再反变换。直接一直在变换域做点值乘法,只判断g [ t ] [ d ] g[t][d] g [ t ] [ d ] 
复杂度O ( V log  V + V ⋅ ω ( V ) ) \mathcal{O}(V\log V+V\cdot\omega(V)) O ( V log  V + V ⋅ ω ( V ) ) 
核心代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 vector<int > f (V)  ;       for  (auto  x : a) {	f[x] = 1 ; } vector<int > F (V)  ;   for  (int  i = 1 ; i < V; i++) {	for  (int  j = i; j < V; j += i) { 		F[i] += f[j]; 	} } auto  G = F;  for  (int  t = 1 ; ; t++) {	for  (int  i = 1 ; i < V; i++) { 		G[i] *= F[i];   	} 	int  res = 0 ;    	for  (int  k = 1 ; d * k < V; k++) { 		res += mu[k] * G[d * k]; 	} 	if  (res > 0 ) { 		cout << (t + n - 1 ) << endl; 		return ; 	} } 
完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 #include  <bits/stdc++.h>  using  namespace  std;constexpr  int  N = 1e4 ;vector<int > prime; int  minp[N];   int  mu[N];     void  solve ()      int  n;     cin >> n;     vector<int > a (n)  ;     int  d = 0 ;     for  (int  i = 0 ; i < n; i++) {         cin >> a[i];         d = gcd (d, a[i]);     }     int  cnt = count (a.begin (), a.end (), d);     if  (cnt) {         cout << (n - cnt) << endl;         return ;     }     const  int  V = *max_element (a.begin (), a.end ()) + 1 ;     vector<int > f (V)  ;            for  (auto  x : a) {         f[x] = 1 ;     }     vector<int > F (V)  ;        for  (int  i = 1 ; i < V; i++) {         for  (int  j = i; j < V; j += i) {             F[i] += f[j];         }     }     auto  G = F;       for  (int  t = 1 ; ; t++) {         for  (int  i = 1 ; i < V; i++) {             G[i] *= F[i];           }         int  res = 0 ;            for  (int  k = 1 ; d * k < V; k++) {             res += mu[k] * G[d * k];         }         if  (res > 0 ) {             cout << (t + n - 1 ) << endl;             return ;         }     } } int  main ()      cin.tie (nullptr )->sync_with_stdio (false );     minp[1 ] = 1 ;     mu[1 ] = 1 ;     for  (int  i = 2 ; i < N; i++) {         if  (!minp[i]) {             minp[i] = i;             prime.push_back (i);             mu[i] = -1 ;         }         for  (auto  p : prime) {             if  (i * p >= N) break ;             minp[i * p] = p;             if  (p == minp[i]) {                 mu[i * p] = 0 ;                 break ;             }             mu[i * p] = -mu[i];         }     }     for  (int  t = (cin >> t, t); t--; )         solve ();     return  0 ; } 
评注:
数论中很多东西不太大,比如素数距离和素因子个数等,看似有好几层循环实际上很小。 「恰好」不好做,尝试转化为「至少」或「至多」,再反演得到结果。本题是把 GCD 恰好等于某数转化为 GCD 是某数的倍数。 大概想法是先找出必要条件,再验证充分性。
倒着求出每个数的下界(a x ⩾ a z ,   a y ⩾ a z a_{x} \geqslant a_z,\ a_{y} \geqslant a_{z} a x  ⩾ a z  ,   a y  ⩾ a z  a a a a a a O ( n + q ) \mathcal O(n+q) O ( n + q ) 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 #include  <bits/stdc++.h>  using  namespace  std;void  solve ()      int  n, q;     cin >> n >> q;     vector<int > b (n)  ;     for  (int  i = 0 ; i < n; i++) {         cin >> b[i];     }     vector<array<int , 3>> que (q);     for  (auto & [x, y, z] : que) {         cin >> x >> y >> z;         x--, y--, z--;     }     reverse (que.begin (), que.end ());     auto  a = b;     for  (auto & [x, y, z] : que) {         a[y] = max (a[y], a[z]);         a[x] = max (a[x], a[z]);         if  (z != x && z != y) {                a[z] = 0 ;         }     }     reverse (que.begin (), que.end ());     auto  c = a;     for  (auto & [x, y, z] : que) {         c[z] = min (c[x], c[y]);     }     if  (b != c) {         cout << -1  << endl;         return ;     }     for  (int  i = 0 ; i < n; i++) {         cout << a[i] << " " ;     }     cout << endl; } int  main ()      cin.tie (nullptr )->sync_with_stdio (false );     for  (int  t = (cin >> t, t); t--; )         solve ();     return  0 ; } 
这题方法应该不少,写一个相对好理解的。
先置a i : = a i ⊕ b i , r e s : = r e s ⊕ b i a_{i}:=a_{i}\oplus b_{i},\mathrm{res}:=\mathrm {res}\oplus b_{i} a i  : = a i  ⊕ b i  , r e s : = r e s ⊕ b i  a i a_{i} a i  
考虑a a a a = 1 a =1 a = 1 a = 1 a =1 a = 1 c = 1 c=1 c = 1 
如果有多位,就要知道每一个二进制位的决定权在谁手上。倒序遍历(因为要找最后一个位置)a a a a i a_{i} a i  2 d 2^{d} 2 d d d d c i c_{i} c i  
现在,从高到低遍历每个二进制位,这一位d d d c = 1 c=1 c = 1 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 #include  <bits/stdc++.h>  using  namespace  std;using  uint = unsigned ;using  ll = long  long ;using  ull = unsigned  long  long ;using  ulll = unsigned  __int128;constexpr  int  B = 60 ;void  solve ()      int  n;     cin >> n;     vector<ull> a (n)  ;     for  (int  i = 0 ; i < n; i++) {         cin >> a[i];     }     ull res = 0 ;     for  (int  i = 0 ; i < n; i++) {         ull x;         cin >> x;         a[i] ^= x;         res ^= x;     }     string c;     cin >> c;     array<ull, B> h{};        array<bool , B> op{};     for  (int  i = n - 1 ; i >= 0 ; i--) {         auto & x = a[i];         for  (int  bit = B - 1 ; bit >= 0 ; bit--) {             if  (x >> bit & 1 ) {                 if  (!h[bit]) {                     op[bit] = c[i] - '0' ;                     h[bit] = x;                     break ;                 } else  {                     x ^= h[bit];                 }             }         }     }     for  (int  bit = B - 1 ; bit >= 0 ; bit--) {         if  (op[bit] ^ (res >> bit & 1 )) {             res ^= h[bit];         }     }     cout << res << endl; } int  main ()      cin.tie (nullptr )->sync_with_stdio (false );     for  (int  t = (cin >> t, t); t--; )         solve ();     return  0 ; }