bzoj P3944: Sum 杜教筛

Description

这里写图片描述

Input

一共T+1行
第1行为数据组数T(T<=10)
第2~T+1行每行一个非负整数N,代表一组询问
Output

一共T行,每行两个用空格分隔的数ans1,ans2
Sample Input

6

1

2

8

13

30

2333
Sample Output

1 1

2 0

22 -2

58 -3

278 -3

1655470 2

分析:杜教筛模版题。

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <map>
#define LL long long

const int maxn=5e6+7;

using namespace std;

LL test,n,cnt;
LL phi[maxn],mul[maxn],g[maxn];
LL f[maxn];
LL prime[maxn],not_prime[maxn];

map <LL,LL> a,b;

void getf(LL n)
{
    phi[1]=1;
    mul[1]=1;
    for (LL i=2;i<=n;i++)
    {
        if (!not_prime[i])
        {
            prime[++cnt]=i;
            phi[i]=i-1;
            mul[i]=-1;
        }
        for (LL j=1;j<=cnt;j++)
        {
            if (i*prime[j]>n) break;
            not_prime[i*prime[j]]=1;
            if (i%prime[j]==0)
            {
                phi[i*prime[j]]=phi[i]*prime[j];
                mul[i*prime[j]]=0;
                break;
            }
            else
            {
                phi[i*prime[j]]=phi[i]*(prime[j]-1);
                mul[i*prime[j]]=-mul[i];
            }
        }
    }
    for (LL i=1;i<=n;i++)
    {
        f[i]=f[i-1]+phi[i];
        g[i]=g[i-1]+mul[i];
    }
}

LL sum_phi(LL n)
{
    if (n<=5e6) return f[n];
    LL c=a[n];
    if (c) return c;
    LL sum=0;
    for (LL i=2,last;i<=n;i=last+1)
    {
        last=n/(n/i);
        sum+=sum_phi(n/i)*(last-i+1);
    }
    c=n*(n+1)/2-sum;
    a[n]=c;
    return c;
}

LL sum_mul(LL n)
{
    if (n<=5e6) return g[n];
    LL c=b[n];
    if (c) return c;
    LL sum=0;
    for (LL i=2,last;i<=n;i=last+1)
    {
        last=n/(n/i);
        sum+=sum_mul(n/i)*(last-i+1);
    }
    c=1-sum;
    b[n]=c;
    return c;
}

int main()
{
    scanf("%lld",&test);
    getf(5e6);
    while (test--)
    {
        scanf("%lld",&n);
        printf("%lld %lld\n",sum_phi(n),sum_mul(n));
    }
}


猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/80738158