扩大
缩小

【题解】CF1547F Array Stabilization (GCD version)

原题链接:https://codeforces.ml/contest/1547/problem/F

题意

有 $n$ 个数 $a_1,a_2,...,a_n$(这里略有改动,原题是下标为 $0 \sim n-1$,此处为方便变成 $1 \sim n$)。

每次新设立一个数组 $b$,且 $b_i = \gcd(a_i,a_{i+1})$,特别地 $b_n=\gcd(a_n,a_1)$,然后将序列 $b$ 的值都赋值给 $a$。

问需要操作多少次,使得 $a_1=a_2=...=a_n$。

$n \le 2 \times 10^5$,$1 \le a_i \le 10^6$。

分析

首先我们需要知道一个结论:$\gcd(a,b,c)=\gcd(\gcd(a,b),c)$。

简略证明如下:

可以将 $a,b,c$ 质因数分解。

设 $p_i$ 为从小到大第 $i$ 个质数,$a=\prod\limits_{i} p_i^{A_i}$,$b=\prod\limits_{i} p_i^{B_i}$,$c=\prod\limits_{i} p_i^{C_i}$,其中 $A_i,B_i,C_i$ 均为非负整数。

那么可以进行推导。

$$
\begin{aligned}
右式 &= \gcd\left(\gcd\left({\prod\limits_{i} p_i^{A_i},\prod\limits_{i} p_i^{B_i}}\right),c\right) \\
&= \gcd \left(\prod\limits_{i} p_i^{\min(A_i,B_i)},c\right)\\
&= \gcd \left(\prod\limits_{i} p_i^{\min(A_i,B_i)},\prod\limits_{i} p_i^{C_i}\right) \\
&= \prod\limits_{i} p_i^{\min(A_i,B_i,C_i)} \\
&= 左式 \\
\end{aligned}
$$

断环成链

我们发现由于首尾将序列 $b$ 的构造方式是把序列 $a$ 在环上求,所以为了方便,我们将 $a$ 复制,接到原来的 $a$ 尾部。

继续分析

如果我们是第一次操作,那么 $b_i=\gcd(a_i,a_{i+1})$,此时我们暂时不将 $b$ 赋给 $a$,先拿数组 $c$ 保存。

第二次操作,有 $b_i=\gcd(c_i,c_{i+1})=\gcd\left(\gcd(a_i,a_{i+1}),\gcd(a_{i+1},a_{i+2})\right)=\gcd(a_i,a_{i+1},a_{i+2})$。

然后就可以发现第 $k$ 次操作有 $b_i=\gcd{a_i,a_{i+1},a_{i+2},...,a_{i+k}}$,这一行 $a$ 是初始序列

如果想知道要几次操作使得 $a_1,a_2,...,a_{n}$ 相等,相当于求需要几次操作,使得所有的 $b_i$ 均等于 $\gcd(a_1,a_2,...,a_n)$。

那么如果这个次数不够,那么至少存在一个 $b_i$ 的值不等于(即大于) $\gcd(a_1,a_2,...,a_n)$。

综上所述,我们需要求一个最小的 $k$,使得存在一个满足 $1 \le i \le n$ 的 $i$,有 $\gcd(a_i,a_{i+1},...,a_{i+k-1})=\gcd(a_1,a_2,...,a_n)$,此时答案即为 $k-1$。

算法选择

如果我们对每个位置 $i$ 按顺序暴力寻找 $k$,那复杂度为 $n^2 \log \max\{a_i\}$ 的,显然不行。

发现,如果满足 $\gcd(a_i,a_{i+1},...,a_{i+k-2})=\gcd(a_1,a_2,...,a_n)$,那么一定有 $\gcd(a_i,a_{i+1},...,a_{i+k-1})=\gcd(a_1,a_2,...,a_n)$。

即存在一个 $j$,若 $k \le j$ 其 $\gcd$ 的值等于 $\gcd(a_1,a_2,...,a_n)$,反之 $k>j$ 时二者相等。

所以,对于每个 $i$ 我们可以二分查找最小的 $k$。

最后还存在一个问题,就是逐步求 $\gcd(a_i,a_{i+1},a_{i+2}...)$ 这步会拖慢程序的效率。

观察到原始的 $a_i$ 不会改变,且多次给同一个 $a_i$ 求 $\gcd$ 不影响结果,我们可以使用 ST 表。

先对已经破环的链存入 ST 表进行预处理,在查询的时候只需要 $O(\log \max\{a_i\})$ 的复杂度(用于求 $\gcd$)。

这个做法时间复杂度为 $\mathcal{O}(n \log n \log \max\{a_i\})$,在 $4$ 秒的限制下可以轻松通过。

代码

 

#include <bits/stdc++.h>
#define INF 1e9
#define eps 1e-6
typedef long long ll;
using namespace std;
 
int t, n, a[400010], GCD, ans, st[400010][20];
bool b[400010], bb;
 
int gcd(int x, int y){
    if(!y) return x;
    return gcd(y, x % y);
}
 
int query(int i, int j){    // ST 表查询
    int k = log2(j - i + 1);
    return gcd(st[i][k], st[j - (1 << k) + 1][k]);
}
 
int main(){
 
    cin >> t;
    while(t--){
        cin >> n, ans = bb = 0;
        for(int i = 1; i <= n; i++)
            cin >> a[i], b[i] = 0;
        for(int i = 1; i <= n; i++) a[i + n] = a[i], st[i][0] = st[i + n][0] = a[i];    // 破环为链
        for(int i = 1; i <= n; i++)
            if(a[i] != a[1]){
                bb = 1;
                break;
            }
        if(!bb){
            puts("0");
            continue;
        }
        GCD = a[1];
        for(int i = 2; i <= n; i++) GCD = gcd(GCD, a[i]);
        for(int j = 1; (1 << j) <= n + n; j++)    // 注意这里是 n + n,ST 表需要处理整条链
            for(int i = 1; i + (1 << j) - 1 <= n + n; i++)
                st[i][j] = gcd(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
        for(int i = 1, L, R, mid; i <= n; i++){
            L = i, R = n + i - 1;
            while(L < R){    
                int mid = (L + R) >> 1;
                if(query(i, mid) != GCD) L = mid + 1;
                else R = mid;
            }
            ans = max(ans, L - i);
        }
        printf("%d\n", ans);
    }
 
    return 0;
}

 

posted @ 2021-07-11 19:08  HoshizoraZ  阅读(180)  评论(2编辑  收藏  举报