找新朋友 |
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) |
Total Submission(s): 3657 Accepted Submission(s): 1681 |
Problem Description
新年快到了,“猪头帮协会”准备搞一个聚会,已经知道现有会员N人,把会员从1到N编号,其中会长的号码是N号,凡是和会长是老朋友的,那么该会员的号码肯定和N有大于1的公约数,否则都是新朋友,现在会长想知道究竟有几个新朋友?请你编程序帮会长计算出来。
|
Input
第一行是测试数据的组数CN(Case number,1 |
Output
对于每一个N,输出一行新朋友的人数,这样共有CN行输出。
|
Sample Input
2 25608 24027 |
Sample Output
7680 16016 |
刚开始自己想的就是直接暴力得了,而且是从老朋友入手。即先把老朋友的数量给暴力出来,然后用CN-老朋友数得结果。后来发现,这个思路不管怎么换算法,都是n^2级别,必定超时。
然后就再次思考题目,发现了题目真实的用以:新朋友编号和会长编号互质。所以呢,又开始往互质上面下功夫。
两个数互质,即两个数没有公共质因子,即两个数最大公约数为1.
具体实现方法就是辗转相除法(欧几里得算法)拿公约数,对公约数是否为1进行判断从而得出结论。
下面是辗转相除法判断两个数是否互质的函数实现:
void IsHz(int a, int b){
int tmp,r;
//先判断一下a,b的大小,约定一下a是较大的数
if(a < b){
tmp = a;
a = b;
b = tmp;
}
//辗转相除拿最大公约数
while(r=a%b){
a = b;
b = r;
}
// cout << "最大公约数是:" << b << endl;
//判断最大公约数是否等于1,得出结论
if(b==1){
cout << "YES" << endl;
} else{
cout << "NO" << endl;
}
}
本以为这样应该可以AC了,结果还是超时。 我已想不到更好的办法,只有看看别人是怎么写的了。
搜索结果表明这道题目是欧拉函数的典型应用,欧拉函数谁什么鬼!!
好吧,然后开始补什么是欧拉函数:
欧拉函数是指:对于一个正整数n,小于n且与n互质的正整数(包括1)的个数,记为S(n),并且有Sn = n*(1-1/p1)*(1-1/p2)*(1-1/p3)*......*(1-1/pn)。
其中p1,p2,p3......pn为n的所有质因子,n是不为0的整数。且S(1) = 1(唯一和1互质的数就是1本身)
对于质数p,有S(p) = p-1
欧拉定理:对于互质的正整数a和n,有 a^S(n) ≡ 1(mod n)
对于模n同余的概念有些忘记了:
两个整数a,b,若他们除以整数m所得的余数相等,则称a,b对于模m同余,记作 a ≡ b(mod m)。读作a同余b模m,或a与b关于m同余。
关于欧拉函数还有很多性质,这里就不展开了。
然后,我就想怎么实现呢?自己的想法是,先把CN的的质因子拿出来,然后按照公式计算。显然这是最傻的办法咯~后来看了这个童鞋的解题报告http://blog.csdn.net/u011514451/article/details/44946789,代码如下:
#include
int oula(int n)
{
int i,sum=n;
for(i=2;i<=n;i++)
if(n%i==0)
{
sum-=sum/i;
while(n%i==0)
{
n/=i;
}
}
return sum;
}
int main()
{
int s,n;
scanf("%d",&s);
while(s--)
{
scanf("%d",&n);
printf("%d\n",oula(n));
}
return 0;
}
sum -= sum/i 刚开始一直不能理解,其实就是欧拉定理。把和CN具有同样质因子的数(不包括1,循环初始条件就说明了这个问题)去掉了。
典型的欧拉函数、欧拉定理的应用~
第一次写解题笔记,这个题花了不少时间,大神勿喷~