B - Count a * b
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;
}