数论(约数之和) - Sigma Function - LightOJ 1336
题意:
根据算数基本定理,对任意正整数n,可分解为:
n=p1a1×p2a2×...×pkak,其中p1,p2,...,pk是n的素因子,ai≥0。
约数之和:
σ(n)=p1−1p1a1+1−1×p2−1p2a2+1−1×...×pk−1pkak+1−1
=(1+p11+p12+...+p1a1)×(1+p21+p22+...+p2a2)×...×(1+pk1+pk2+...+pkak)
计算区间[1,n]内,有多少个正整数i,满足i的约数之和σ(i)是偶数。
输入:
T组测试数据,
每组包括一个正整数n。
输出:
一个正整数,表示答案。
Sample Input
4
3
10
100
1000
Sample Output
Case 1: 1
Case 2: 5
Case 3: 83
Case 4: 947
数据范围:
T≤100,1≤n≤1012
分析:
对于每个正整数n,求约数之和的时间复杂度为O(n
),
若对[1,n]内的每个数都暴力统计一次,显然是不能够的。
我们考虑从σ(n)的特征入手。
σ(n)是乘积的形式,我们要判断其奇偶性,我们知道:
①、偶数×偶数=偶数
②、偶数×奇数=偶数
③、奇数×奇数=奇数
由此可见,σ(n)为奇数的可能性是比较少的。
所以,我们可以求出[1,n]内,约数之和为奇数的数的个数cnt,再用总数n−cnt,得到答案。
下探究σ(n)何时为奇数:
需满足对n的任意质因子pi,1+pi1+pi2+...+piai为奇数。
在所有的素数中,仅2为偶数,其他素数均为奇数。
①、当n有素因子2时,1+21+22+...+2a1恒为奇数。
②、对于其他素因子pi,要使1+pi1+pi2+...+piai为奇数,必有pi1+pi2+...+piai为偶数,
首先pik必为奇数,偶数个奇数相加为偶数,故ai必为偶数。
也就是说,对于任意的pi=2,1+pi1+pi2+...+piai为奇数当且仅当ai为偶数。
总结:σ(n)为奇数,当且仅当n的所有素因子(除了2以外)的指数ai均为偶数,
若存在素因子2,对因子2的指数无要求。
由于ai为偶数,我们记ai=2ai′,则n=2k×(p1a1′)2×(p2a2′)2×...×(pkak′)2
其中k为任意非负整数,我们发现,(p1a1′)2×(p2a2′)2×...×(pkak′)2是一个完全平方数,
当k为偶数时,n就是一个完全平方数;当k为奇数时,n是一个完全平方数的两倍。
综上:σ(n)为奇数,当且仅当n是一个完全平方数或是一个完全平方数的两倍。
因此,我们仅需统计出[1,n]以内,所有是完全平方数和完全平方数的2倍的数的个数,即为cnt。答案为n−cnt。
可以证明,一个完全平方数的2倍一定不是完全平方数,因此统计这两种数不会产生冲突。这里可以简化代码。
注意:
我们最后要输出n−cnt,n是long long级别的,最后要以长整型格式输出。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
int T;
ll n;
int main()
{
cin>>T;
for(int t=1;t<=T;t++)
{
cin>>n;
int cnt=0;
for(ll i=1;i*i<=n;i++,cnt++)
if(2*i*i<=n) cnt++;
printf("Case %d: %lld\n",t,n-cnt);
}
return 0;
}