poj 2478 Farey Sequence 线性筛法优化的欧拉函数

一:题意:
给定一个数n,求在[1,n]这个范围内两两互质的组合数,该题其实就是求,[2,n]分区间内的所以数组的欧拉函数之和,其中n (2 <= n <= 106)。
二:欧拉函数的概念
1,欧拉函数:
对于一个正整数n,小于n且和n互质的正整数(包括1)的个数,叫做该数的欧拉函数,记作φ(n) 。例如φ(8)=4,因为1,3,5,7均和8互质。
2,欧拉函数计算:
(1) 通式:φ(x)=x * (1-1/p1) * (1-1/p2) * (1-1/p3)…… (1-1/pn),其中p1, p2……pn为x的所有质因数,x是不为0的整数。
(2) 特例:φ(1)=1。
(3) 注意:每种质因数只一个。比如12=2*2*3那么φ(12)=12*(1-1/2)*(1-1/3)=4
3,欧拉函数的性质:
(1) 若n是质数p的k次幂,φ(n)=p^k-p^(k-1)=(p-1)p^(k-1)。因为除了p的倍数外,其他数都跟n互质。
(2) 若m,n互质,φ(mn)=φ(m)φ(n)。
(3) 当n为奇数时,φ(2n)=φ(n).
欧拉打表最重要的两个性质:
(4) 对于质数p,φ(p) = p - 1。 注意φ(1)=1.
(5) 设a为N的质因数,
若(N % a == 0 && (N / a) % a == 0) 则有: E(N)=E(N / a) * a;
若(N % a == 0 && (N / a) % a != 0) 则有:E(N) = E(N / a) * (a - 1)。
这里可以看出p和a都是质数(素数),所以我们可以利用素数的先行筛选法来加快欧拉打表。
三:素数的线性筛选

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
using namespace std;
const int Max=100000;
bool isPrime[Max+2];// 标记素数
int prime[Max+2];//记录素数
int total=0;//素数个数记录
void Prime()
{
    memset(isPrime,true,sizeof(isPrime));
    total=0;
    for(int i=2; i<=Max; i++)
    {
        if(isPrime[i])
            prime[total++]=i;//放入表中
        for(int j=0; j<total && i*prime[j]<=Max; j++)
        {
            isPrime[i*prime[j]]=false;//利用合数必能分解出一个素数
            if(i%prime[j]==0)
                break;//防止重挖
        }
    }
    printf("%d\n",total);
}
int main()
{
    Prime();
    return 0;
}

四:线性欧拉打表代码(poj2478):

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
const int Max=1000010;
long long str[Max];//欧拉表
int used[Max];//素数标记
int prime[Max];//素数记录
int N,Size;

void print()
{//线性打表
    memset(used,true,sizeof(used));
    Size=0;
    for(int i=2;i<=1000000;i++)
    {
        if(used[i])
        {
            str[i]=i-1;//i是素数的情况
            prime[Size++]=i;
        }
        for(int j=0;j<Size&& i*prime[j]<1000002;j++)
        {
            used[i*prime[j]]=false;
            if(i%prime[j])//欧拉两个打表定理
                 str[prime[j]*i]=str[i]*(prime[j]-1);
            else
            {
                 str[prime[j]*i]=str[i]*prime[j];
                 break;
            }
        }
    }
    for(int i=3;i<=1000000;i++)
        str[i]=str[i]+str[i-1];
}

int main()
{
    print();
    while(scanf("%d",&N)!=EOF && N)
    {
        printf("%lld\n",str[N]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值