返回顶部

AcWing - 220 - 最大公约数 = 积性函数筛

https://www.acwing.com/problem/content/222/

求n以内gcd为质数的数对个数。

其实要是n和m不一样的话,貌似是要下面这样做的。也就是n和m不一样可能都得反演,下面这个是nlogn左右的,瓶颈在于给每个T刷上他的质因子p对应的mu[T/p]。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int MAXN = 1e7;
int Sum[MAXN + 5];

char mu[MAXN + 5];
int p[(MAXN) / 10 + 5], ptop;
bitset < MAXN + 5 > np;

void sieve(int n = MAXN) {
    np[1] = mu[1] = 1;
    for(int i = 2; i <= n; i++) {
        if(!np[i])
            p[++ptop] = i, mu[i] =  - 1;
        for(int j = 1, t; j <= ptop && (t = i * p[j]) <= n; j++) {
            np[t] = 1;
            if(i % p[j])
                mu[t] = -mu[i];
            else {
                mu[t] = 0;
                break;
            }
        }
    }
    for(int i = 1; i <= ptop; ++i) {
        for(int cnt = 1, x = p[i]; x <= n; x += p[i], ++cnt) {
            Sum[x] += mu[cnt];
        }
    }
    for(int i = 1; i <= n; ++i) {
        Sum[i] += Sum[i - 1];
    }
}

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku
    int n;
    scanf("%d", &n);
    sieve(n);
    ll sum = 0;
    for(int l = 1, r; l <= n; l = r + 1) {
        r = n / (n / l);
        sum += 1ll * (n / l) * (n / l) * (Sum[r] - Sum[l - 1]);
    }
    printf("%lld\n", sum);
}

上网找了一个奇怪的线性筛,开眼界,什么都可以筛,根据最小质因子的个数进行分类讨论,这种解法是可以解决n和m不一样的问题的。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int MAXN = 1e7;
int Sum[MAXN + 5];

char mu[MAXN + 5];
int p[(MAXN) / 10 + 5], ptop;
bitset < MAXN + 5 > np;

void sieve(int n = MAXN) {
    //Sum[T]表示T的所有质数因子p的mu[T/p]之和
    np[1] = mu[1] = 1;
    Sum[1] = 0;
    for(int i = 2; i <= n; i++) {
        if(!np[i]) {
            p[++ptop] = i, mu[i] =  - 1;
            Sum[i] = mu[1];
        }
        for(int j = 1, t; j <= ptop && (t = i * p[j]) <= n; j++) {
            np[t] = 1;
            if(i % p[j]) {
                mu[t] = -mu[i];
                Sum[t] = -Sum[i] + mu[i];
            } else {
                mu[t] = 0;
                //T有>=2个p
                //当T有2个p时,只有除掉p可能会有结果,此时为Sum[T]=mu[y]
                //当T有>=3个p时,结果都是0,此时也是Sum[T]=mu[y]
                Sum[t] = mu[i];
                break;
            }
        }
    }

    for(int i = 1; i <= n; ++i) {
        Sum[i] += Sum[i - 1];
    }
}

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku
    int n;
    scanf("%d", &n);
    sieve(n);
    ll sum = 0;
    for(int l = 1, r; l <= n; l = r + 1) {
        r = n / (n / l);
        sum += 1ll * (n / l) * (n / l) * (Sum[r] - Sum[l - 1]);
    }
    printf("%lld\n", sum);
}

但是貌似因为n和m一样就可以简化。直接使用欧拉函数就完事了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int MAXN = 1e7;

ll phi[MAXN + 5];
int p[MAXN / 10 + 5], ptop;

void sieve(int n) {
    phi[1] = 1;
    for(int i = 2; i <= n; i++) {
        if(!phi[i])
            p[++ptop] = i, phi[i] = i - 1;
        for(int j = 1, t; j <= ptop && (t = i * p[j]) <= n; j++) {
            if(i % p[j])
                phi[t] = phi[i] * (p[j] - 1);
            else {
                phi[t] = phi[i] * p[j];
                break;
            }
        }
    }
    for(int i = 1; i <= n; ++i) {
        phi[i] += phi[i - 1];
    }
}

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku
    int n;
    scanf("%d", &n);
    sieve(n);
    ll res = 0;
    for(int i = 1; i <= ptop; ++i) {
        res += 2ll * phi[n / p[i]] - 1ll;
    }
    printf("%lld\n", res);
}

需要注意的是bitset比bool慢多了,bitset只是对比整数可能会有优势,或者拿来省空间,但实际上在线性筛里面都可以对待筛函数赋值一个奇怪的东西使这个数组变的不必要。

posted @ 2019-09-02 22:57  Inko  阅读(183)  评论(0编辑  收藏  举报