找新朋友

来源

http://139.196.145.92/contest_show.php?cid=1890#problem/C

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
前置知识:
  1. 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)}... ]

分析问题:
  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
原文地址:https://www.cnblogs.com/Arno-vc/p/13722545.html