1. 程式人生 > >【bzoj4804】歐拉心算 歐拉函數

【bzoj4804】歐拉心算 歐拉函數

rim .cn pre 情況 fine true lin () load

題目描述

給出一個數字N

技術分享

輸入

第一行為一個正整數T,表示數據組數。 接下來T行為詢問,每行包含一個正整數N。 T<=5000,N<=10^7

輸出

按讀入順序輸出答案。

樣例輸入

1
10

樣例輸出

136


題解

歐拉函數

技術分享

其中用到了技術分享

這個推導很簡單:由歐拉函數的定義,$\sum\limits_{i=1}^k\sum\limits_{j=1}^i[\gcd(i,j)=1]=\sum\limits_{i=1}^k\varphi(i)$,此時$i\ge j$,而當$i\le j$時情況相同。最後減掉重復計算的(1,1)即為左邊。

然後剩下的就好說了,預處理歐拉函數$\varphi$和其前綴和$sum$,分塊枚舉$\lfloor\frac nd\rfloor$的取值並計算即可。

#include <cstdio>
#include <algorithm>
using namespace std;
#define N 10000010
typedef long long ll;
const int m = 10000000;
int prime[N] , tot , phi[N];
ll sum[N];
bool np[N];
int main()
{
	int i , j , t , n , last;
	ll ans;
	sum[1] = phi[1] = 1;
	for(i = 2 ; i <= m ; i ++ )
	{
		if(!np[i]) phi[i] = i - 1 , prime[++tot] = i;
		for(j = 1 ; j <= tot && i * prime[j] <= m ; j ++ )
		{
			np[i * prime[j]] = 1;
			if(i % prime[j] == 0)
			{
				phi[i * prime[j]] = phi[i] * prime[j];
				break;
			}
			else phi[i * prime[j]] = phi[i] * (prime[j] - 1);
		}
		sum[i] = sum[i - 1] + phi[i];
	}
	scanf("%d" , &t);
	while(t -- )
	{
		scanf("%d" , &n) , ans = 0;
		for(i = 1 ; i <= n ; i = last + 1) last = n / (n / i) , ans += (sum[last] - sum[i - 1]) * sum[n / i];
		printf("%lld\n" , 2 * ans - sum[n]);
	}
	return 0;
}

【bzoj4804】歐拉心算 歐拉函數