来源
Description
新年快到了,“猪头帮协会”准备搞一个聚会,已经知道现有会员N人,把会员从1到N编号,其中会长的号码是N号,凡是和会长是老朋友的,那么该会员的号码肯定和N有大于1的公约数,否则都是新朋友,现在会长想知道究竟有几个新朋友?请你编程序帮会长计算出来。
Input
输入:第一行是测试数据的组数CN(Case number,1<cn<10000),接着有CN行正整数N(1<n<32768),表示会员人数。
Output
输出:对于每一个N,输出一行新朋友的人数,这样共有CN行输出。
Sample Input
2
25608
24027
Sample Output
7680
16016
前置知识:
-
Elur函数
对于正整数n,欧拉函数是小于或等于n的正整数中与n互质的数的数目.如:(varphi (8)=4),为1,3,5,7
性质:
- 若p为质数,显然(varphi(p)=p-1)
- 若(p=a^b(a,b,p in N^*))(a为质数),(varphi(p)=a^b-a^{(b-1)}=(a-1)*a^{(b-1)}),因为一共有(n=a^b)个数,想要获得所有与n互质的数,只需要除去a的倍数即可.这里简单地解释一下(a^{(b-1)}):设有n中有m个a的倍数,则有(ma=a^b => m=a^{(b-1)}).
- Elur函数是积性函数,即:(varphi(mn)=varphi(m)*varphi(n)(m,n互质))
若(p=a_1^{b_1}*a_2^{b_2}*a_3^{b_3}...(a_i为质数{p,a_i,b_i|p,a_i,b_i in N^*})),由算术基本定理可知,这里的p可以表示所有的正整数.那么,根据以上三条性质可得:
[varphi(p)=(a_1-1)*(a_2-1)*(a_3-1)...a_1^{(b_1-1)}*a_2^{(b_2-1)}*a_3^{(b_3-1)}... ]
分析问题:
- 显然只要套一下公式即可
代码:
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
ll getNum(ll n);
int main()
{
ll n,num,ans;
scanf("%lld",&num);
while(num--){
scanf("%lld",&n);
ans = getNum(n);
printf("%lld
",ans);
}
return 0;
}
ll getNum(ll n) //计算n中的
{
ll num=1;
for(int i=2;i*i<=n;i++){ //相等的时候也需要包含在内,否则出来的时候只加一次
if(n%i==0){ //这个内外的乘法参考了晚上的代码,很巧妙,没有对于a^(i-1)次做额外的判断
n/=i;
num*=(i-1); //这里乘了a
while(n%i==0){ //设n=i^a+j,这边乘了a-1次
n/=i;
num*=i;
}
}
}
if(n>1){
num*=n-1;
}
return num;
}
注:
- 这边的质数i的遍历是从2开始依次向上遍历的,但合数,如:4不会影响到结果,因为n已经被质数2筛过了
- wa了两发,都是因为
i*i<n
,因为如果没有包括i*i=n的范围
,那么最后的num*=n-1
,有时应该是num*=(n-1)*n