P1621 集合

 欧拉筛先把质数整出来,现在需要一个执行将任意具有不小于p的公共质因子的整数合并.

我并没有看到什么求公共质因子的算法,而且就算有也只能想到O(N2)的合并方法.

根据欧拉筛的启示,有如下方法:

  对于查找到的每一个质数pi>=p,合并pi,2pi,3pi...(直到即将越界).

之后统计集合个数即得答案.

这样操作之后可以使得任意以pi为公共质因子的整数处在同一个集合,现在只需要考虑另外一种情况,(假设各个pi形成的集合仍是相互独立的)即pi,pj两集合(设为A,B)是否合并.

当遍历合并pi的倍数时,可以肯定,如果pi*pj没有越界,此时A,B将合并,否则一定不会合并.pi*pj不越界是A,B集合合并的充分必要条件.

  首先,遍历pi的倍数kp时,当k<pj,kp一定不会与pj有公因数.当k=pj,kp与pj一定有且仅有公因数pj.

所以这个情况已经被包含在一般处理中了.

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;

bool isPrime[100010];
int a, b, p, fa[100010], ans;
vector<int> s;

int get(int x) {
    if (fa[x] == x) return x;
    return fa[x] = get(fa[x]);
}
void merge(int x, int y) { fa[get(x)] = fa[get(y)]; }
void init(int n) {
    for (int i = 1; i <= b; i++) fa[i] = i;
    fill(isPrime, isPrime + 100010, 1);
    isPrime[1] = isPrime[0] = 0;

    for (int i = 2; i <= n; i++) {
        if (isPrime[i]) s.push_back(i);
        for (int j = 0; j < s.size() && i * s[j] <= n; j++) {
            isPrime[i * s[j]] = 0;
            if (i % s[j] == 0) break;
        }
    }
}

int main() {
    scanf("%d%d%d", &a, &b, &p);
    init(b);

    for (auto it = s.begin(); it != s.end(); it++) {
        if (*it < p) continue;
        for (int i = 2; i * (*it) <= b; i++) merge(*it, i * (*it));
    }

    // 统计答案
    bool used[100010] = {0};
    for(int i = a; i <= b; i++) used[get(i)] = true;
    for(int i = a; i <= b; i++) if(used[i]) ans++;
    printf("%d
", ans);

    return 0;
}
P1621
原文地址:https://www.cnblogs.com/Gaomez/p/14371789.html