POJ2480Longge's problem 欧拉函数

                                         Longge's problem

Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 7737 Accepted: 2565
Description
Longge is good at mathematics and he likes to think about hard mathematical problems which will be solved by some graceful algorithms. Now a problem comes: Given an integer N(1 < N < 2^31),you are to calculate ∑gcd(i, N) 1<=i <=N.

“Oh, I know, I know!” Longge shouts! But do you know? Please solve it.
Input
Input contain several test case.
A number N per line.
Output
For each N, output ,∑gcd(i, N) 1<=i <=N, a line
Sample Input
2
6
Sample Output
3
15
Source
POJ Contest,Author:Mathematica@ZSU

首先,∑gcd(i, N)一定是对N的因数求和。N的因数一定少于N个,所以不用线性枚举N的每个因数。假设m个数1<=i1,i2,…im小于N和N的最大公因数都是i,那么只需求其中一个和N 的最大公因数即可,其余的不用求,这样剩下不少操作。
问题转化为:求满足gcd(i,n)=d的i的个数。假设有m个,记作i1,i2,…im。
则:gcd(i1/d,n/d)=1
gcd(i2/d,n/d)=1

gcd(im/d,n/d)=1
所以,m为小于n/d且和n/d互质的数的个数,即n/d的欧拉函数值。
∑igcd(i, N)= ∑dd*phi(N/d)
那么我们就需要得到不同的N的因数d。显然要用到素数,而且是多cases,所以先素数打表。
从素数2开始枚举素数到sqrt(n),只要是N的因数d,则N/d也一定是N 因数,则就计算d*phi(N/d)和N/d*phi(d)。
那么枚举因数即可。

#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<cstring>
#include<cmath>

#include<algorithm>
#define LL long long 
using namespace std;
const int maxn=1000010;
int pri[maxn];
int num=0;
int n;
bool flag[maxn];
//素数打表,详细见:http://blog.csdn.net/dafang_xu/article/details/49873757
void getprime(){  
    for(int i=2;i<=maxn;i++){
        if(!flag[i])pri[num++]=i;
        for(int j=0;j<num&&i*pri[j]<=maxn;j++){
            flag[pri[j]*i]=true; 
            if(i%pri[j]==0) break;
        }
    }
} 
LL phi(LL n){
    LL ans=n; 
    for(int i=0;i<num&&pri[i]*pri[i]<=n;i++){
        if(n%pri[i]==0){
            ans=ans/pri[i]*(pri[i]-1);
            while(n%pri[i]==0)n/=pri[i];
        }
    }
    if(n>1)ans=ans/n*(n-1);
    return ans;
}

int main(){
    freopen("i.txt","r",stdin); 
    getprime(); 
    while(scanf("%d",&n)!=EOF){
        LL ans=0;
        int tmpn=sqrt(n*1.0);
        for(int i=1;i<=tmpn;i++){//*
            if(i*i==n)ans+=i*phi(n/i);
            else if(n%i==0){
                ans=ans+i*phi(n/i);
                LL tmp=n/i;
                ans+=tmp*phi(i);
            }
        }
        printf("%I64d\n",ans);
    }
    return 0;
} 
PS:代码“//*”处,如果循环写成for(int i=1;i*i<= n;i++)就TLE!
写成for(int i=1;i<= sqrt(n*1.0);i++)AC266ms
写成   int tmpn=sqrt(n*1.0);
        for(int i=1;i<=tmpn;i++)   AC 94ms
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值