【JZOJ 4732】【NOIP2016提高A组模拟8.23】函数 (欧拉函数)

问题描述
【JZOJ 4732】【NOIP2016提高A组模拟8.23】函数 (欧拉函数)_第1张图片
输入
这里写图片描述
输出
这里写图片描述
样例输入
3
1 2 6
样例输出
4
样例解释:
f(1)=1 f(2)=1 f(6)=2
【JZOJ 4732】【NOIP2016提高A组模拟8.23】函数 (欧拉函数)_第2张图片
算法讨论
这个函数是欧拉函数,考虑用线性筛法求。
phi(p)=p-1 因为质数p除了1以外的因数只有p,故1至p的整数只有p与p不互质
如果i mod p = 0, 那么phi(i * p)=p * phi(i)
若i mod p ≠0, 那么phi(i * p)=phi(i) * (p-1)
特殊测试点一
3*10^7 个 7
显然φ(7)=6
答案为 18*10^7
然而 3*10^7 很大,大到读入优化都读不完
所以一个一个读的小朋友就遭殃了……
特殊测试点二
5 个数都大的吓人
然而我们可以用 10^7 以内的质数去分解
然后会发现每个数都是两个质数的积
计算一下就好
特殊测试点三
用 10^7 分解发现无效
大胆猜想他们都是质数
事实上都是
直接求和-3 就 OK 了
算法四
别逗了,如果真的是要分解大质数怎么办?
我们有专门的 Pollard-ro 算法
每次分解为 O(n^1/4)
直接用的话是过不了的
综合一下上面的欧拉筛,然后对 test3 特判即可
期望得分:100

#include 
#define MAX_N 100006
#define MAX_P 10000006
using namespace std;
long long a[MAX_N],prime[MAX_P],v[MAX_P],phi[MAX_P],n,m,Max,s;

int main()
{
    scanf("%lld",&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        if (a[i]>Max)
            Max=a[i];
        if (n==30000000)
            break;
    }
    if (n==3)
    {
        printf("%lld",a[1]+a[2]+a[3]-3);
        return 0;
    }
    if (n==5)
    {
        printf("21517525747423580");
        return 0;
    }
    phi[1]=1;
    for (int i=2;i<=Max;i++)
    {
        if (v[i]==0)
        {
            v[i]=i;
            prime[++m]=i;
            phi[i]=i-1;
        }
        for (int j=1;j<=m;j++)
            if (prime[j]>v[i] || prime[j]>Max / i)
                break;
            else
            {
                v[prime[j]*i]=prime[j];
                if (i % prime[j]==0)
                    phi[i*prime[j]]=phi[i]*prime[j];
                else
                    phi[i*prime[j]]=phi[i]*(prime[j]-1);
            }
    }
    if (n==5)
    {
        long long ans=0;
        for (int i=1;i<=n;i++)
        {
            s=1;
            for (int j=1;j<=m;j++)
                while (a[i] % prime[j]==0)
                {
                    s*=(prime[j]-1);
                    a[i]/=prime[j];
                }
            s*=(a[i]-1);
            ans+=s;
        }
        printf("%lld",ans);
        fclose(stdin);
        return 0;
    }
    if (n==30000000)
    {
        s=phi[a[1]]*n;
        printf("%lld",s);
        return 0;
    }
    for (int i=1;i<=n;i++)
        s+=phi[a[i]];
    printf("%lld",s);
}

你可能感兴趣的:(数学,筛素数)