51nod-1239-欧拉函数之和(杜教筛,积性函数前缀和)

链接:

https://www.51nod.com/Challenge/Problem.html#problemId=1239

题意:

对正整数n,欧拉函数是小于或等于n的数中与n互质的数的数目。此函数以其首名研究者欧拉命名,它又称为Euler's totient function、φ函数、欧拉商数等。例如:φ(8) = 4(Phi(8) = 4),因为1,3,5,7均和8互质。

S(n) = Phi(1) + Phi(2) + ...... Phi(n),给出n,求S(n),例如:n = 5,S(n) = 1 + 1 + 2 + 2 + 4 = 10,定义Phi(1) = 1。由于结果很大,输出Mod 1000000007的结果。

思路:

欧拉函数前缀和
对于欧拉函数首先有\(n = \sum_{d|n} \varphi(d)\), 所以可以得到\(\varphi(n) = n - \sum_{d|n, d<n} \varphi(d)\)
所以我们有

\[\sum_{i=1}^n \varphi (i) = \sum_{i=1}^n (i - \sum_{d|i, d<i} \varphi (d)) = \sum_{i=1}^n i - \sum_{i=2}^n \sum_{d|i, d<i} \varphi (d) \]

此时我们可以枚举\(\frac{i}{d}\)

\[\sum_{i=1}^n i - \sum_{\frac{i}{d} = 2}^n \sum_{d=1}^{\lfloor \frac{n}{\frac{i}{d}} \rfloor} \varphi (d) \]

再把\(\frac{i}{d}\)看成\(i\),得到

\[\sum_{i=1}^n i - \sum_{i=2}^n \sum_{d=1}^{\lfloor \frac{n}{i} \rfloor} \varphi (d) \]

得到

\[sum(n) = \sum_{i=1}^n i - \sum_{i=2}^n sum(\lfloor \frac{n}{i} \rfloor) \]

使用记忆化搜索和hash存储的方式减少重复运算,分块处理每个\(\frac{n}{i}\)

代码:

//#include<bits/stdc++.h>
#include<iostream>
#include<string>
#include<cstdio>
#include<vector>
#include<string.h>
#include<cstring>
#include<set>
#include<queue>
#include<algorithm>
#include<math.h>
#include<stdio.h>
#include<map>
#include<stack>
#include<list>
#define INF 0x3f3f3f3f
#define pii pair<int, int>
#define pll pair<LL, LL>
#define mp make_pair
#define pb push_back
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int MOD = 1e9+7;
const int HMOD = 999959;
const int VMOD = 5000000;
const int MAXN = 1010;
const int INV = 500000004;
const double eps = 1e-8;
int Next[4][2] = {-1, 0, 0, 1, 1, 0, 0, -1};

vector<pll> h[HMOD+5];
LL phi[VMOD+5];
bool isp[VMOD+5];
int pri[VMOD+5], cnt = 0;
LL n;

void init()
{
    //欧拉函数打表
    memset(isp, 0, sizeof(isp));
    phi[1] = 1;
    for (int i = 2;i <= VMOD;i++)
    {
        if (!isp[i])
        {
            pri[++cnt] = i;
            phi[i] = i-1;
        }
        for (int j = 1;j <= cnt && i*pri[j] <= VMOD;j++)
        {
            isp[pri[j]*i] = 1;
            if (i%pri[j] == 0)
            {
                phi[i*pri[j]] = phi[i]*pri[j]%MOD;
                break;
            }
            phi[i*pri[j]] = phi[i]*(pri[j]-1)%MOD;
        }
    }
    for (int i = 2;i <= VMOD;i++)
        phi[i] = (phi[i-1]+phi[i])%MOD;
}

void upd(LL n, LL res)
{
    h[n%HMOD].pb(mp(n, res));
}

LL cal(LL n)
{
    if (n <= VMOD) return phi[n];
    for (pll pa: h[n%HMOD]) if (pa.first == n)
        return pa.second;
    //记忆化搜索,hash记录
    LL ans = 0;
    for (LL l = 2;l <= n;)
    {
        LL x = n/l;
        LL r = n/x;
        ans = (ans + (r-l+1)%MOD*cal(x)%MOD)%MOD;
        l = r+1;
    }
    ans = (((n%MOD*((n+1)%MOD)%MOD)*INV%MOD-ans)%MOD+MOD)%MOD;
    upd(n, ans);
    return ans;
}

int main()
{
    IOS;
    init();
    scanf("%lld", &n);
    printf("%lld", cal(n));

    return 0;
}


posted @ 2020-02-09 14:17  YDDDD  阅读(431)  评论(0编辑  收藏  举报