积性函数性质-POJ2480

https://vj.xtuacm.cf/contest/view.action?cid=146#problem/D

参考:http://www.cnblogs.com/xiaowuga/p/7161513.html
http://www.cnblogs.com/xiaowuga/p/7161513.html

这个题需要用到欧拉函数的知识……

欧拉函数的定义:对正整数n,欧拉函数是小于n的正整数中与n互质的数的数目(我们定义φ(1)=1)

欧拉函数的的通式:φ(n)=n*(1-1/p1)(1-1/p2)(1-1/p3)……*(1-1/ps)(p1,p2,p3,……ps均是n的质因子)

欧拉函数有这么几个比较重要的性质:

性质1:如果n是质数p的k次幂,那么φ(n)=p^k-1*(p-1)

性质2:欧拉函数是积性函数——若m,n互质,φ(mn)=φ(n)*φ(m),积性函数和完全积性函数有区别,有兴趣可以自己百度一下

性质3:当n为奇数的时候,φ(2n)=φ(n),这一点是由性质2推出来的,因为2必定和所有的奇数都是互质的,所以而φ(2)=1。所以得出这个结果

性质4:n的所有质因子之和等于φ(n)*n/2(这不算性质,只能算延伸)。

好了,大体介绍完了欧拉函数,我们可以开始来看看这个题怎么做了。

首先要知道gcd(i,n)是积性函数(当n固定时),也就是说gcd(i*j,n)=gcd(i,n)*gcd(j,n)(这里还有一个限制条件,就是i,j互质,所以gcd并非完全积性函数)

好了现在我们需要来学习真正的姿势了,我也是刚学的,利用gcd是积性函数的性质,根据前文说的,我们有这样的结论:n>1时 n=p1^a1*p2^a2*…ps^as,p为n的质因子,那么f(n)是积性函数的充要条件是f(1)=1,及f(n) = f(p1^a1)*f(p2^a2)…f(pr^ar),所以只要求f(pi^ai)就好。现在来看具体做法。

f(pi^ai) = Φ(pi^ai)+pi*Φ(pi^(ai-1))+pi^2*Φ(pi^(ai-2))+…+pi^(ai-1)* Φ(pi)+ pi^ai *Φ(1)

根据性质1,我们可以做出如下化简

f(pi^ai)=[pi^(ai-1)(pi-1) ] + [pi*pi^(ai-2)(pi-1)] + [pi^2*pi^(ai-3)(pi-1)] + [pi^3*pi^(ai-4)(pi-1)]….[pi^(ai-1)pi^(ai-ai)(pi-1)]+pi^ai ①

然后对①提取公因子(pi-1)

f(pi^ai)=(pi-1){[pi^(ai-1) ] + [pi*pi^(ai-2)] + [pi^2*pi^(ai-3)] + [pi^3*pi^(ai-4)]….[pi^(ai-1)*pi^(ai-ai)]+[pi^ai/(pi-1)]} ②

紧接着我们发现出了最后一项每个[]每个方括号内乘积都等于pi^(ai-1),所以对②提取公因子pi^(ai-1)

f(pi^ai)=(pi-1)pi^(ai-1){ai+[pi/(pi-1)]} ③

然后把(pi-1)/pi放进括号里得

f(pi^ai)=pi^(ai){1+ai(pi-1)/pi} ④

这个 ④是单个f(pi^ai)的公式,我们提取所有的pi^(ai)相乘实际上就是n了,所以我们可以得到f(n)的公式:f(n)=n*∏(1+ai*(pi-1)/pi)

代码:

#include<stdio.h>
int main()
{
    long long n;
    while(scanf("%I64d",&n)!=EOF)
    {
        long long i;
        long long ans=n;
        for(i=2;i*i<=n;i++)
        {
            if(n%i==0)
            {
                long long a=0;
                while(n%i==0)
                {
                    a++;
                    n/=i;
                }
                ans=ans+ans*a*(i-1)/i;
            }
        }
        if(n>1)
            ans=ans/n*(n+n-1);
        printf("%I64d\n",ans);
    }
    return 0;
}

这是第一次做的代码,用时较长:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll long long
using namespace std;
int cnt,a[100];
ll euler (ll n)
{
    ll ans=1;
    for(ll i=2; i*i<=n; i++)
    {
        if(n%i)
            continue;
        ans*=i-1;
        n=n/i;
        while(n%i==0)
        {
            n=n/i;
            ans*=i;
        }
    }
    if(n>1)ans*=(n-1);
    return ans;
}
void fen(ll n)
{
    ll s=sqrt(n);
    for(ll i=1; i<=s; i++)
    {
        if(n%i==0)
        {
            a[cnt++]=i;
            if(i!=(n/i))
                a[cnt++]=n/i;
        }
    }
}
int main()
{
    ll n;
    while(~scanf("%I64d",&n))
    {
        memset(a,0,sizeof(a));
        cnt=0;
        fen(n);
        ll s=sqrt(n);
        ll ans=0;
        for(ll i=0; i<cnt; i++)
        {
            ans+=(euler(n/a[i])*a[i]);
            //cout<<ans<<endl;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值