SGU102 Coprimes

知識準備

約定 對于一個整數n,小于n且和n互質的正整數(包括1)的個數,記作 dp(n)


性質一 對于素數p,有dp(p)=p-1

對于兩個不同的素數pq,它們的乘積n=p*q滿足 dp(n)=dp(p)*dp(q)(證明略,中國剩余定理)


歐拉函數

任意一個整數n都可以表示成其素因子的乘積

n=(P1^K1)*(P2^K2)*..........*(Pi^Ki)
dp(n)=(P1^(K1-1))P11*(P2^(K2-1))P2-1*.........*(Pi^(Ki-1))(Pi-1)


 


簡要分析

要設計程序,需要盡量減少變量的使用,可以使程序比較簡單明了。那么就要對歐拉函數進行一定的變換,對知識準備的歐拉函數兩個公式進行變換有

dp(n)=n*(1-1/p1)*(1-1/p2)*........*(1-1/pi)


代碼設計

AC也可用笨辦法,直接求素數,然后數一下就可以了

#include<iostream>

int gcd(int a,int b)

{

int r=0;

r
=a%b;

while(r)

{

a
=b;

b
=r;

r
=a%b;

}

return b;

}


int main()

{

int n;

int sum=0;

std::cin
>>n;

for(int i=1;i<=n;++i)

{

if(gcd(n,i)==1)++sum;

}

std::cout
<<sum;

//system("pause");

return 0;

}


應用歐拉函數是如何解決的呢,首先要知道素因數的求法,然后歐拉函數第一個公式,那么我們從素數2開始,舉個例子18/2 9/3 ,3/3那么9的素因子就是23。這樣在由變換的公式累乘就可以了。

#include<iostream>

#include
<math.h>

int prime(int n)

{

for(int i=2;i<=(int)sqrt(n);++i)

{

if(n%i==0)return 0;

}

return 1;

}


int main()

{

int n,flag,d=2;

double ans;

std::cin
>>n;

ans
=n;

if(prime(n)&&n!=1)ans=n-1;

else{

while(n!=1)

{

flag
=0;

while(n%d==0)

{

flag
=1;

n
=n/d;

}

if(flag)ans*=(double)(1-(double)1/(double)d);

do{

++d;

}
while(!prime(d));

}

}

std::cout
<<ans;

return 0;

}


兩種做法對比一下

time memory

34ms 191kb 歐拉函數

71ms 171kb 直接數


總結:對數學知識,像小學的質因數等遺忘了。甚為恐怖,雖然沒學過數論,但這些常識的遺忘敲響了警鐘。還有就是編碼的能力太差,一些技巧需要不斷的學習練習。這次做題也就是一個學習的過程,不求數量。數據結構的設計需要大量的經驗,看來一時也別無他法。一個好的數據結構,可以讓代碼很簡潔,算法效率很高。要知道算法是實施在數據結構之上的