• 【题解】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;
    }
  • 相关阅读:
    curd_4
    curd_2
    Python Regex库的使用
    Python Assert 确认条件为真的工具
    Python Regex库的使用(2)
    Python lambda的用法
    Python 列表综合
    with ss(date,date2) (select * from sysdummy1) select * from ss
    延迟执行函数
    ObjectiveC 的基本数据类型、数字、字符串和集合等介绍
  • 原文地址:https://www.cnblogs.com/HZSPZCR/p/14999323.html
Copyright © 2020-2023  润新知