列表

详情


6464. 最大公约数遍历

给你一个下标从 0 开始的整数数组 nums ,你可以在一些下标之间遍历。对于两个下标 i 和 ji != j),当且仅当 gcd(nums[i], nums[j]) > 1 时,我们可以在两个下标之间通行,其中 gcd 是两个数的 最大公约数 。

你需要判断 nums 数组中 任意 两个满足 i < j 的下标 i 和 j ,是否存在若干次通行可以从 i 遍历到 j 。

如果任意满足条件的下标对都可以遍历,那么返回 true ,否则返回 false 。

 

示例 1:

输入:nums = [2,3,6]
输出:true
解释:这个例子中,总共有 3 个下标对:(0, 1) ,(0, 2) 和 (1, 2) 。
从下标 0 到下标 1 ,我们可以遍历 0 -> 2 -> 1 ,我们可以从下标 0 到 2 是因为 gcd(nums[0], nums[2]) = gcd(2, 6) = 2 > 1 ,从下标 2 到 1 是因为 gcd(nums[2], nums[1]) = gcd(6, 3) = 3 > 1 。
从下标 0 到下标 2 ,我们可以直接遍历,因为 gcd(nums[0], nums[2]) = gcd(2, 6) = 2 > 1 。同理,我们也可以从下标 1 到 2 因为 gcd(nums[1], nums[2]) = gcd(3, 6) = 3 > 1 。

示例 2:

输入:nums = [3,9,5]
输出:false
解释:我们没法从下标 0 到 2 ,所以返回 false 。

示例 3:

输入:nums = [4,3,12,8]
输出:true
解释:总共有 6 个下标对:(0, 1) ,(0, 2) ,(0, 3) ,(1, 2) ,(1, 3) 和 (2, 3) 。所有下标对之间都存在可行的遍历,所以返回 true 。

 

提示:

原站题解

去查看

上次编辑到这里,代码来自缓存 点击恢复默认模板
class Solution { public: bool canTraverseAllPairs(vector<int>& nums) { } };

python3 解法, 执行用时: 576 ms, 内存消耗: 35.8 MB, 提交时间: 2023-05-28 10:58:57

@cache
def f(num):
    temp = []
    p, x = 2, num
    while p * p <= x:
        if x % p == 0:
            temp.append(p)
            x //= p
            while x % p == 0:
                x //= p
        p += 1
    if x > 1:
        temp.append(x)
    return tuple(temp)
    
    
class Solution:
    def canTraverseAllPairs(self, nums: List[int]) -> bool:
        if len(nums) == 1:
            return True

        nums = set(nums)
        if 1 in nums:
            return False

        nums = list(nums)
        n = len(nums)

        ps = set()
        for i, x in enumerate(nums):
            nums[i] = f(x)
            ps |= set(nums[i])

        p2i = dict()
        cnt = 0
        for p in ps:
            p2i[p] = n
            n += 1
            cnt += 1
        
        fa = list(range(n))
        
        def find(x):
            if fa[x] != x:
                fa[x] = find(fa[x])
            return fa[x]
        
        def union(x, y):
            x, y = find(x), find(y)
            fa[x] = y
        
        for i in range(len(nums)):
            c = 0
            for p in nums[i]:
                j = p2i[p]
                if find(i) != find(j):
                    union(i, j)
                    c += 1
            cnt -= max(c - 1, 0)
            if cnt == 1: return True
        return False

python3 解法, 执行用时: 380 ms, 内存消耗: 31.3 MB, 提交时间: 2023-05-28 10:58:26

# -*- coding: utf-8 -*-
from typing import List, Tuple
from collections import deque, Counter
from queue import PriorityQueue
import math
from functools import lru_cache
from sortedcontainers import SortedDict, SortedSet
import random
import copy
import sys
sys.setrecursionlimit(9999999)

MOD = 10**9 + 7


def get_prime(N):
    prime_vals = []
    flag = [True] * (N+1)
    for val in range(2, N+1):
        if flag[val]:
            prime_vals.append(val)

        for p_val in prime_vals:
            if val * p_val > N:
                break

            flag[val * p_val] = False

            if val % p_val == 0:
                break

    return prime_vals

primes = get_prime(1000)

def devide_prime(val):
    ans = []

    for i in primes:
        if i*i > val:
            break

        if val % i == 0:
            cnt = 0
            while val % i == 0:
                val //= i
                cnt += 1

            ans.append((i, cnt))

        if val == 1:
            break

    if val != 1:
        ans.append((val, 1))

    return ans

class Solution:
    def canTraverseAllPairs(self, nums: List[int]) -> bool:
        n = len(nums)
        if n == 1:
            return True

        for v in nums:
            if v == 1:
                return False

        nums = list(set(nums))

        e = set()
        P = set()
        for v in nums:
            ret = devide_prime(v)
            for p, cnt in ret:
                P.add(p)

            k = len(ret)
            for i in range(k):
                for j in range(i+1, k):
                    a, b = ret[i][0], ret[j][0]
                    e.add((a, b))

        p = {}
        for x in P:
            p[x] = x

        def rt(x):
            if p[x] != x:
                p[x] = rt(p[x])
            return p[x]

        def merge(a, b):
            aa, bb = rt(a), rt(b)
            if aa != bb:
                p[aa] = bb

        for a, b in e:
            merge(a, b)

        rt_set = set()
        for x in P:
            rt_set.add(rt(x))
            if len(rt_set) >= 2:
                return False

        return True

cpp 解法, 执行用时: 276 ms, 内存消耗: 91.9 MB, 提交时间: 2023-05-28 10:57:36

/**
 * 把 nums 中的每个位置看成一个点,把所有质数也都看成一个点。
 * 如果 nums[i] 被质数 p 整除,那么从位置点 i 向质数点 p 连一条边。
 * 因为每个数至多只能被 log 个质数整除,因此连边的总数是 O(nlogA) 的。
 * 这样,问题就变为:检查所有位置点是否处于同一连通块内。用并查集解决即可。
 **/
#define MAXX ((int) 1e5)
bool inited = false;
vector<int> fac[MAXX + 10];

// 全局预处理每个数的质因数
void init() {
    if (inited) return;
    inited = true;

    for (int i = 2; i <= MAXX; i++) if (fac[i].empty()) for (int j = i; j <= MAXX; j += i) fac[j].push_back(i);
}

class Solution {
public:
    bool canTraverseAllPairs(vector<int>& nums) {
        init();

        int n = nums.size();
        int mx = 0;
        for (int x : nums) mx = max(mx, x);

        // 初始化并查集
        int root[n + mx + 1];
        for (int i = 0; i <= n + mx; i++) root[i] = i;

        // 查询并查集的根
        function<int(int)> findroot = [&](int x) {
            if (root[x] != x) root[x] = findroot(root[x]);
            return root[x];
        };

        // 对每个 nums[i],向它们的质因数连边
        for (int i = 0; i < n; i++) for (int p : fac[nums[i]]) {
            int x = findroot(i), y = findroot(n + p);
            if (x == y) continue;
            root[x] = y;
        }

        // 检查是否所有位置点都在同一连通块内
        unordered_set<int> st;
        for (int i = 0; i < n; i++) st.insert(findroot(i));
        return st.size() == 1;
    }
};

上一题