zoukankan      html  css  js  c++  java
  • 【题解】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 imes 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=prodlimits_{i} p_i^{A_i}$,$b=prodlimits_{i} p_i^{B_i}$,$c=prodlimits_{i} p_i^{C_i}$,其中 $A_i,B_i,C_i$ 均为非负整数。

    那么可以进行推导。

    $$
    egin{aligned}
    右式 &= gcdleft(gcdleft({prodlimits_{i} p_i^{A_i},prodlimits_{i} p_i^{B_i}} ight),c ight) \
    &= gcd left(prodlimits_{i} p_i^{min(A_i,B_i)},c ight)\
    &= gcd left(prodlimits_{i} p_i^{min(A_i,B_i)},prodlimits_{i} p_i^{C_i} ight) \
    &= prodlimits_{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})=gcdleft(gcd(a_i,a_{i+1}),gcd(a_{i+1},a_{i+2}) ight)=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
    ", ans);
        }
     
        return 0;
    }
  • 相关阅读:
    CentOS7使用集群同步脚本对文件同步分发
    CentOS7安装jdk1.8
    CentOS7+CDH5.12.1集群搭建
    nginx通配符
    Nginx反向代理及配置
    一些好玩有用的书签
    linux操作小技巧锦集
    系统压测结果对比:tomcat/thinkphp/swoole/php-fpm/apache
    python修改linux日志(logtamper.py)
    【原创】给定随机数的取值范围(最小值、最大值),且要求多次取得的随机数最后的结果有一个固定的平均值
  • 原文地址:https://www.cnblogs.com/zengpeichen/p/14999323.html
Copyright © 2011-2022 走看看