经典小知识融合数论题(hdu-5528)

B - Count a * b

 HDU - 5528 

Marry likes to count the number of ways to choose two non-negative integers a and b less than m to make a×b mod m≠0.

Let's denote f(m) as the number of ways to choose two non-negative integers a and b less than m to make a×b mod m≠0. 

She has calculated a lot of f(m) for different m, and now she is interested in another function g(n)=∑m|nf(m)g(n)=∑m|nf(m). For example, g(6)=f(1)+f(2)+f(3)+f(6)=0+1+4+21=26. She needs you to double check the answer. 

经典小知识融合数论题(hdu-5528)



Give you n. Your task is to find g(n) modulo 2^64.

Input

The first line contains an integer T indicating the total number of test cases. Each test case is a line with a positive integer n. 

1≤T≤20000
1≤n≤10^9

Output

For each test case, print one integer ss, representing g(n)g(n) modulo 264264.

Sample Input

2
6
514

Sample Output

26
328194

这道题就是让你求在 a和b在(0到n-1)范围内有多少a*b%n不等以0,求出不等于0的个数。

由题意显然可以得0到n-1内一共有n^2个a*b的组合。观察图中的给出的示例:table4:

我们可以观察到第几行和6的的gcd是多少,本行中出现的0的次数就为几。这个可以通过gcd(每次跳几步)*n(一共跳n步)/(在n的范围内)。又因为gcd肯定为n的因子。所以出现的0的次数可以得到。为本行的值和n的gcd。我们又看出第0行所有的值是0,同时0和n无法求gcd。但是我们可以将第0行看作第n行,这样使所有的行满足情况。

公式如下:

经典小知识融合数论题(hdu-5528)

最后我们可以通过g(n)求解。

 

要想做出这道题,首先你要先知道几个推论:

phi(x)为x的欧拉值。

1〉i从1到n:gcd(i,n)求和 =  phi(n/d)*d  (d为n的因子)求和

我们可以知道n的gcd肯定是n的因子,1到n与n的gcd为1的数量为phi(n),1到n与n的gcd的为2的数量等于与(n/2)的gcd值为1的所以为phi(n/2)......则可以得到上式。

2〉交换求和次序

3〉x的因子的欧拉和等于x

可以通过欧拉函数的性质得到(用质因子分解求欧拉函数值的过程)。求n的欧拉就是用n减去和n的gcd为n的因子(不包括1)的数的个数,因为1到n内所有数和n的gcd都是n的因子。这样和n的gcd为n的因子的数全加一边就是1到n的所有数了。

4〉将累加转换累乘(本质用质因子分解优化)

nn  为n的质因子个数,pi为质因子的值,ki的pi的指数

我们都知道n的因子和为 [pi^(ki+1)-1]/(pi-1),   i从1到nn的累乘。

则n的因子的平方和也可得。

n的因子个数为   i从1到nn , (ki+1)的累乘。

代码如下:

#include<bits/stdc++.h>
const int maxn = 1e5+5;
typedef long long ll;
using namespace std;
int prime[maxn],cnt;
bool isprime[maxn];
void init()
{
    cnt=0;
	for(ll i=2;i<maxn;i++)
	{
		if(!isprime[i])
		{
		   prime[cnt++] = i;
		   for(ll j=i*i;j<maxn;j+=i)
		   isprime[j] =	true;
		}
	}
} 
int main()
{
	init();
	int t,num;
	ll n,tn,tm;
	scanf("%d",&t);
	while(t--)
	{
		tn=1;
		tm=1;
		scanf("%lld",&n);
		ll nn = n;
		for(int i=0;i<cnt&&prime[i]*prime[i]<=nn;i++)
		{
			if(nn%prime[i])
			continue;
		    num=0;
		    ll tt = prime[i];
			while(nn%prime[i]==0) {
			nn/=prime[i];
			tt*=prime[i];
			num++;
		   }
		   ll a=(tt-1)/(prime[i]-1),b=tt+1,c=prime[i]+1;
		   tn*=(num+1);
		   if(a%c==0) tm*=a/c*b;
		   else tm*=b/c*a;
		}
		if(nn>1)
		{
		tn*=2;
		tm*=(nn*nn+1);
	    }
		printf("%lld\n",tm-tn*n);
	}
	return 0;
}