欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

51Nod-1237-最大公约数之和 V3

程序员文章站 2024-02-22 12:33:40
...

ACM模版

描述

51Nod-1237-最大公约数之和 V3

题解

发现 51Nod 上题号为 123 的几个题连着都是杜教筛,俨然可以成为一个模版搞搞了……但是我的数学水平实在有限,就算搞了模版怕是也无法灵活使用,所以想想也就算了。

其实早先我是不知道分块具体是指什么的,做了这几道杜教筛的问题后,我算是搞明白这个,就是我们最后获取的公式是一个递归式,所以我们可以将整个问题划分为两部分,小的部分打表出来,而大的部分,可以依靠递归式来求解,当然,这也许只是我的一个狭隘的认知,实在是对这些个概念懵懵懂懂的。

具体的推导过程,给大家分享一个大牛的链接,实在是对 LaTeX 的公式写的手抽筋儿,所以懒得再絮絮叨叨的写一堆推导了,dance_in_the_dark’s blog 公式推导相当详细,大牛说很容易就推出来了,这让我这数学 zz 和公式恐惧症很尴尬啊……只能 Orz 一下了,希望有一天我也可以将自己的数学水平提高到不再畏惧~~~

代码

#include <iostream>
#include <cstdio>

#define ll long long

using namespace std;

const int MAXN = 1e6 + 5;
const int MOD = 1e9 + 7;
const int INV_2 = 5e8 + 4;

ll pri[MAXN];
ll vis[MAXN];
ll phi[MAXN];
ll h[MAXN * 10];
ll f[MAXN * 10];
ll n, ans;

int hash_(ll x)
{
    int t = x % MAXN;
    while (h[t] && h[t] != x)
    {
        t = (t + 1) % MAXN;
    }
    return t;
}

ll Gphi(ll n)
{
    if (n <= MAXN)
    {
        return phi[n];
    }

    ll x = hash_(n);
    if (h[x])
    {
        return f[x];
    }

    ll t = n % MOD;
    ll k = t * (t + 1) % MOD * INV_2 % MOD;
    for (ll i = 2; i <= n; i = t + 1)
    {
        t = n / (n / i);
        k -= Gphi(n / i) * (t - i + 1) % MOD;
    }
    k = (k % MOD + MOD) % MOD;
    h[x] = n;
    f[x] = k;
    return k;
}

void init()
{
    phi[1] = 1;
    for (int i = 2; i <= MAXN; i++)
    {
        if (!vis[i])
        {
            pri[++pri[0]] = i;
            phi[i] = i - 1;
        }
        for (int j = 1; j <= pri[0]; j++)
        {
            if (i * pri[j] > MAXN)
            {
                break;
            }

            vis[i * pri[j]] = 1;
            if (i % pri[j] == 0)
            {
                phi[i * pri[j]] = phi[i] * pri[j];
                break;
            }
            else
            {
                phi[i * pri[j]] = phi[i] * phi[pri[j]];
            }
        }
    }
    for (int i = 1; i <= MAXN; i++)
    {
        phi[i] += phi[i - 1];
    }
}

int main()
{
    init();

    scanf("%lld", &n);

    ll t, l, k;
    for (ll i = 1; i <= n; i = t + 1)
    {
        t = n / (n / i);
        k = (Gphi(t) - Gphi(i - 1) + MOD) % MOD;
        l = n / i % MOD;
        ans = (ans + l * l % MOD * k % MOD) % MOD;
    }

    printf("%lld\n", ans);

    return 0;
}
相关标签: 杜教筛 分块