zoukankan      html  css  js  c++  java
  • 欧拉函数

    Phi(x)表示小于等于x的数中与x互质的数的个数。

    x的质因数是k1,k2…   则Phi(x)=x*(1-1/k)*(1-1/k2)*  …

    例如Phi(6)=6*(1-1/3)*(1-1/2) =2

    1.很显然,一个质数的Phi值等于它-1

    2.如果x是质数的话 Phi(x^k)= x^k-x^k/x=x^k-x^(k-1),因为和这个数不互质的只有x的倍数。

    3.欧拉函数是积性函数,如果把x因数分解后x=k1^p1*k2^p2*…*kn^pn

    那么Phi(x)=Phi(k1^p1)*Phi(k2^p2)+...+Phi(kn^pn)

    这个式子就可以用2中的公式快速计算。

    4.假设j是i的质因数

    那么i整除j^2时Phi(i)=phi(i/j)*j  因为Phi(i/j)中就已经计算了(1-1/j)

    否则i不能整除时j^2时Phi(i)=Phi(i/j)*(j-1) 因为Phi(i/j)中不曾计算(1-1/j),

    计算:1.√n暴力 2.筛法的时候,对质数k都枚举它的倍数x之后phi(x)=phi(x)/k*(k-1),复杂度是Onloglogn的(log的log)

    实现:

    1.模板题  hdu2824 求∑Phi(x) l<=x<=r  l,r<=3000000

    #include<iostream>
    #include<cstdio>
    #define ll long long
    #define MAXN 3000000
    using namespace std;
    ll phi[MAXN+5];
    int l,r;
    int main()
    {
        for(int i=1;i<=MAXN;i++)phi[i]=i;
        for(int i=2;i<=MAXN;i++) if(phi[i]==i)
        {   phi[i]=i-1;
            for(int j=i<<1;j<=MAXN;j+=i)
                phi[j]=phi[j]/i*(i-1);
        }
        for(int i=1;i<=MAXN;i++)phi[i]+=phi[i-1];
        while(scanf("%d%d",&l,&r)!=EOF)
           cout<<phi[r]-phi[l-1]<<endl;
        return 0;
    }

    2.poj2480 Longge's problem

    题意:求∑gcd(i,N) 1<=i<=N  N是int

    题解:

    对于每个它的因数k,gcd(x,N) 的x的个数为Phi(x/k),贡献为k*Phi(x/k),所以可以直接暴力,复杂度大概是√n*因数个数。

    那有没有更优秀的做法呢?当然是有的。

    设f[n]为题目所求的∑gcd(i,N)  因为gcd是积性函数,又根据一个结论:积性函数求和还是积性函数,所以f(x)也是积性函数。

    我们可以把n分解成p1^k1*p2^k2*....*pm^km,则f[n]=f[p1^k1]*...*f[pm^km]

    根据f[x]=∑k*Phi(x/k)  k|x 我们可以得到:f[p^k]=∑p^i*Phi(P^(k-i)) i=0..k  根据第二点化简一下:

    f[p^k]=∑ p^i*(P^(k-i)-P^(k-i-1))(i=1..k)+p^k  

        =∑(p^k-P^(k-1))(i=1..k)+p^k

        =(k+1)*p^k-k*p^(k-1)

    这样只要直接因数分解就能非常快的算出答案,最后把所有的f值乘起来就可以了。

    #include<iostream>
    #include<cstdio>
    #define MAXN 100000
    #define ll long long
    using namespace std;
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    
    int n,cnt=0;
    ll ans=1;
    int s[MAXN+5];
    bool b[MAXN+5];
    
    int pow(int x,int p)
    {
        int sum=1;
        for(int i=x;p;p>>=1,i*=i)if(p&1)sum=sum*i;
        return sum;
    }
    
    int main()
    {
        n=read();
        for(int i=2;i<=MAXN;i++)
        {
            if(!b[i])s[++cnt]=i;
            for(int j=1;j<=cnt&&s[j]*i<=MAXN;j++)
            {
                b[s[j]*i]=1;
                if(i%s[j]==0)break;
            }
        }
        for(int i=1;i<=cnt;i++)
            if(n%s[i]==0)
            {
                int num=0;
                while(n%s[i]==0)
                {n/=s[i];num++;}
                ans*=1LL*(num+1)*pow(s[i],num)-1LL*num*pow(s[i],num-1);
            }
        if(n>1)ans*=2*n-1;
        cout<<ans;
        return 0;
    }

    3.欧拉函数前缀和

    原题是这样的:有个n*n的矩阵,每个点有一个灭的灯泡。

    你每次可以按下一个灯泡(x,y)的开关,这样的话所有(x*k,y*k),1<=k<=n/x 的灯泡状态都会改变。

    就比如n=4,你按下(2,1)就会改变(2,1),(4,2)的状态。

    求你最少要按下多少次,可以让所有的灯都亮起来。

    题解:

    我们发现,对于每个不同的x/y我们都只会按一次,所以我们只会去按那些xy互质的点。

    题目转化为有多少对(x,y)是互质的,即(∑Phi(i),2<=i<=n)*2+1(也可以假装Phi(1)是1),就是一道欧拉函数前缀和题目,但n有10^9那么大,无法暴力。

    那么我们用F(x)表示有多少个gcd(x,y)=1 1<=y<=x的个数,那么gcd(x,y)=2的个数为F(x/2),gcd=3的个数为F(x/3)....

    由于状态总共有x^2种 所以F(x)+F(x/2)+F(x/3)+...+F(x/x)=x^2 

    也就是说F(x)=x^2-F(x/2)-F(x/3)-...-F(1)   

    F(i)的i最多只有√n种,所以可以记忆化搜索,据说复杂度是n^(2/3),很科学。

    但是这题貌似是某高校的contest,是Private的,所以你也没处交.....所以代码就假装会了懒得写了...

    UPD:这道题在51nod上有,戳我啊 

  • 相关阅读:
    ASP.NET MVC preview 1升级到ASP.NET MVC preview 2的一些更改
    今天遇到一个非常奇怪的问题
    Microsoft ASP.NET MVC中Membership登陆的实现
    自己用的一个ASP.Net MVC分页拿出来分享下
    KnockoutJs学习笔记(一)
    KnockoutJs学习笔记(三)
    KnockoutJs学习笔记(二)
    KnockoutJs学习笔记(四)
    学习网站不定期更新
    一些好的网站
  • 原文地址:https://www.cnblogs.com/FallDream/p/oula.html
Copyright © 2011-2022 走看看