經典小知識融合數論題(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. 



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行,這樣使所有的行滿足情況。

公式如下:

最後我們可以通過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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章