CF1325E Ehab's REAL Number Theory Problem

发布时间 2023-11-07 12:02:37作者: -白简-

题目传送门

题目大意

给定 \(n\) 个数,每个数的因数个数不超过 \(7\),求最少选出多少个数能使得乘积为一个完全平方数。

无解输出 \(-1\)

思路

约数个数定理:对于

\[n=\prod^{k}_{i=1}p_i^{a_i} \]

\(n\) 的正约数个数为 \(\prod^{k}\limits_{i=1}(a_i + 1)\)

那么可以转化题目中的条件,含有 \(3\) 个本质不同质因数的数含有 \(8\) 个因子,那么因数个数不超过 \(7\) 可以转化为每个数至多\(2\) 个本质不同的质因子。

容易发现,如果将每个数自身的平方因子都去掉,不会影响最终的答案,我们只需要考虑出现次数不为偶数的因数。

对于一个数 \(n\),可以表示为 \(p^x \cdot q^y\)(不保证 \(p,q \neq 1\))。将平方因子除尽后,显然有 \(x,y \in \left\{ 0,1 \right\}\),我们把 \(1\) 也看作一个质因数,对于每个已经除尽平方因子的数 \(n'\),我们在 \(p,q\) 间连边,每一条边表示原序列中的一个数。

可以发现,如果我们能找到一个子图,满足每个点的度数均为偶数,这样的子图的边数就是我们寻找的答案。

环显然满足这种性质,我们想使得答案最小,所以寻找最小环的大小。

一般的求最小环方法是 \(O(n ^ 3)\) 的 Floyd,显然是不行的。

考虑枚举起点 BFS 求最小环,但这样复杂度也是 \(O(n ^ 2)\) 的,考虑优化。

\(V = \max\left\{a_i\right\}\),我们发现,值大于 \(\sqrt{V}\) 的质数是不会与其他结点连边的,无需将其当作起点枚举。

时间复杂度 \(O(n \sqrt{V})\)

Code

Code
// CF1325E
#include <bits/stdc++.h>

using namespace std;

#define int long long

const int N = 2005000;
const int M = 2000000;
const int INF = 0x3f3f3f;

int n;

struct Edge{
    int next,to;
}e[N << 1];

int cnt,h[N];

void Add(int u,int v) {
    cnt ++;
    e[cnt].next = h[u];
    h[u] = cnt;
    e[cnt].to = v;
}

bool p[N];
int tot,pri[N >> 2];
int pos[N],lim = 0;

void Seive() {
    p[1] = 1;
    pos[1] = 1;

    for (int i = 2;i <= M; i++) {
        if(!p[i]) {
            tot ++;
            pri[tot] = i;
            pos[i] = tot + 1;

            if(i < 1000)
                lim ++;
        }

        for (int j = 1;j <= tot && i * pri[j] <= M; j++) {
            p[i * pri[j]] = 1;

            if(i % pri[j] == 0)
                break;
        }
    }

    return ;
}

void Divide(int x) {
    int p = 0,q = 0,limit = sqrt(x);

    for (int i = 2;i <= limit; i++) {
        if(x % i == 0) {
            int cnt = 0;

            while(x % i == 0) {
                cnt ++;
                x /= i;
            }

            if(cnt & 1) {
                if(p == 0)
                    p = i;
                else
                    q = i;
            }
        }
    }

    if(x != 1) {
        if(p == 0)
            p = x;
        else
            q = x;
    }

    if(p == 0 && q == 0) {
        cout << 1 << endl;
        exit(0);
    }

    if(q == 0) {
        Add(pos[p],1);
        Add(1,pos[p]);
    }
    else {
        Add(pos[p],pos[q]);
        Add(pos[q],pos[p]);
    }

    return ;
}

int dis[N],ans = LONG_LONG_MAX;

void bfs(int s) {
    queue<pair<int,int>> que;
    memset(dis,0x3f3f3f,sizeof(dis));

    dis[s] = 0;
    que.emplace(s,0);

    do {
        int x = que.front().first,fa = que.front().second;
        que.pop();

        for (int i = h[x];i; i = e[i].next) {
            int to = e[i].to;

            if(to == fa)
                continue;
            
            if(dis[to] >= INF) {
                dis[to] = dis[x] + 1;
                que.emplace(to,x);
            }
            else if(dis[x] + dis[to] > 0)
                ans = min(ans,dis[to] + dis[x] + 1);
        }
    }while(!que.empty());
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    Seive();
    
    cin >> n;
    for (int i = 1,x;i <= n; i++) {
        cin >> x;
        Divide(x);
    }
    
    for (int i = 1;i <= lim; i++) 
        bfs(i);
    
    if(ans > n)
        cout << "-1" << endl;
    else
        cout << ans << endl;

    return 0;
}