• Codeforces 1325E Ehab's REAL Number Theory Problem


    Description

    给一些数,每个的因数个数不超过 $7$,求最少选出多少个,使得乘积为完全平方。无解输出 $-1$。

    Solution

    「每个的因数个数不超过 $7$」看上去非常玄学,它的本质是什么?

    唯一分解定理:任何一个大于 $1$ 的自然数 $N$,可以唯一分解成有限个质数的乘积。即:

    $$N = prod_{i=1}^{n} p_i ^{k_i}$$

    这里 $forall 1 le i le n, k_i ge 1$,$p_1 < p_2 < cdots <p_n$,且 $forall 1le i le n, p_i in ext{prime} $,正确性显然。

    约数个数定理:任何一个大于 $1$ 的自然数 $N$,它的约数个数为:

    $$ d(N) = prod_{i=1}^{n}(k_i + 1) $$

    显然对于每一种质因子,都有选 $0$ 个,选 $1$ 个……选 $k_i$ 个共 $(k_i + 1)$ 种选法,根据乘法原理,当然是它们的乘积。

    好,我们用一下约数个数定理。设想 $a_i$ 有 $3$ 种质因数,那么 $d(a_i)$ 最小应当是 $(1+1)^3 = 8$,和 $d(a_i) le 7$ 矛盾,所以,$a_i$ 至多有 $2$ 种质因子。

    所以,$a_i$ 要么是 $1$,要么可以表示为 $p_1^{k_1}$ 的形式,要么可以表示为 $p_1^{k_1} cdot p_2^{k_2}$ 的形式。不难发现把 $k$ 对 $2$ 取模不会对答案造成影响,这是因为一个数本身的平方因子可以直接相消了。

    现在,$a_i$ 只会是 $1$,$p_1$,$p_1 cdot p_2$ 三种可能了。

    • 如果一个 $a_i = 1$,直接输入 $1$,结束程序,因为直接选它就好了;
    • 如果一个 $a_i = p_1$,那么,建一条边,把 $1$ 和 $p_1$ 连起来;
    • 如果一个 $a_i = p_1cdot p_2$,那么,建一条边,把 $p_1$ 和 $p_2$ 连起来。

    注意 $p$ 的值可能很大,需要离散化,可以在线性筛的时候预处理。

    现在得到了一个图,答案就是这个图的 最小环 的大小。

    为什么?选择一个数,就是选择一条边,如果我们选了一个环,那么环上的每个 $p_i$ 都有两条边相连,也就是乘了 $2$ 次,那么这当然是一个完全平方数了!

    $10^6$ 以内质数大概是 $78500$ 个,这个数字记作 $P$。

    用 Floyd 算法 $mathcal O(P^3)$ 求最小环是行不通的。

    因为边权为 $1$,可以枚举起点然后 BFS,当然,这样直接做的复杂度是 $mathcal O(nP)$,当然也是行不通的。(枚举起点 $mathcal O(P)$,单次 BFS 是 $mathcal O(n)$,因为有 $n$ 条边)

    深入剖析,发现 一个环内必然有一个点 $le sqrt{max a_i}$,这是因为不会有两个大于 $sqrt{max a_i}$ 的点之间有连边,只要这个较小数作为起点被枚举了,那么这个环就必然会被 BFS 到。所以,起点只要枚举到 $sqrt{max a_i} = 10^3$ 即可。

    当然,必然点都是质数,所以我们记 $10^3$ 以内的质数个数为 $P'$,这个算法的时间复杂度就是 $mathcal O(nP')$,可以通过。

    当然我的代码偷个懒是直接枚举到 $sqrt{max a_i} = 10^3$ 的……

    #include <bits/stdc++.h>
    #define REP(i, x, y) for(register int i = x; i <= y; i++)
    using namespace std;
    const int N = 1e5 + 5, A = 1e6 + 5, SQRTA = 1000;
    const int INF = 0x3f3f3f3f;
    int n, ncnt, hd, tl, que[N][2], ans = INF;
    int a[N], prm[N], id[A], h[N], dis[N];
    bool npr[A];
    struct edge
    {
        int v, nxt;
    } e[A << 1];
    void EulerSieve()
    {
        for(int i = 2; i < A; i++)
        {
            if(!npr[i]) prm[++prm[0]] = i, id[i] = prm[0] + 1;
            for(int j = 1; j <= prm[0] && i * prm[j] < A; j++)
            {
                npr[i * prm[j]] = true;
                if(i % prm[j] == 0) break;
            }
        }
    }
    inline void AddEdge(int u, int v)
    {
        e[++ncnt] = (edge){v, h[u]}; h[u] = ncnt;
        e[++ncnt] = (edge){u, h[v]}; h[v] = ncnt;
    }
    void Divide(int x)
    {
        int p[4] = {0}, k[4] = {0}; 
        for(int i = 1; i <= prm[0] && prm[i] * prm[i] <= x; i++)
        {
            if(x % prm[i] == 0)
            {
                p[++p[0]] = prm[i];
                while(x % prm[i] == 0) k[p[0]] ^= 1, x /= prm[i];
                if(!k[p[0]]) p[0]--;
            }
        }
        if(x > 1) p[++p[0]] = x, k[p[0]] = 1;
        if(!p[0]) { cout << 1 << endl; exit(0); }
        else if(p[0] == 1) AddEdge(1, id[p[1]]);
        else AddEdge(id[p[1]], id[p[2]]);
    }
    void Bfs(int s)
    {
        memset(dis, 0x3f, sizeof dis);
        dis[s] = 0;
        que[1][0] = s; que[1][1] = 0;
        hd = tl = 1;
        while(hd <= tl)
        {
            int u = que[hd][0], fa = que[hd][1]; 
            hd++;
            for(int i = h[u]; i; i = e[i].nxt)
            {
                int v = e[i].v;
                if(v == fa) continue;
                if(dis[v] == INF)
                {
                    tl++;
                    que[tl][0] = v; que[tl][1] = u;
                    dis[v] = dis[u] + 1;
                }
                else ans = min(ans, dis[u] + dis[v] + 1);
            }
        }
    }
    int main()
    {
        cin >> n;
        REP(i, 1, n) cin >> a[i];
        EulerSieve();
        REP(i, 1, n) Divide(a[i]);
        REP(i, 1, SQRTA) Bfs(i);
        if(ans == INF) cout << -1 << endl;
        else cout << ans << endl;
        return 0;
    }
  • 相关阅读:
    Python3三位运算
    PyThon3函数的使用
    PyThon3类的基本使用
    PyThon3类的继承
    Python3方法重写
    【BZOJ3307】雨天的尾巴-线段树合并+树上差分
    【CF893F】Subtree Minimum Query-主席树
    【BZOJ2212】Tree Rotations(POI2011)-平衡树启发式合并
    【BZOJ2733】永无乡(HNOI2012)-平衡树启发式合并
    【BZOJ3160】万径人踪灭-FFT+Manacher
  • 原文地址:https://www.cnblogs.com/syksykCCC/p/CF1325E.html
Copyright © 2020-2023  润新知