Pillai's arithmetic function 算法实现 --------------------------------附 河南省2017多校联萌5-CSDN博客

81 阅读2分钟

Pillai's arithmetic function

定义:求前n个数分别和n的最大公约数的和。详细见维基百科(下图)

\

那么如何高效求出函数值P(n)呢?且n很大,例如 n := 1:1e11。(取10^11是为了秒出结果,因为开方后不超过百万)

这个数论问题,具体实现需要用到因数分解,线性素数筛,质因数分解,欧拉函数,也得知道这个公式。

\

1、先因数分解,我算了下,10^11的因数才只有144个。

2、然后就是求单个欧拉数,不可能用线性欧拉筛,存不下,也没时间。只能需要哪个求哪个(在线算法)。

单个欧拉数求法,直接用定义。百度百科

3、要快速求出欧拉数,就需要质因数分解,求出素因数。这求需要线性素数筛求p[i].虽然n很大,但是开方一次就不过百万了。

4、得到素因数,进而求出欧拉数,再进而求出 Pillai's arithmetic function 值。

\

输入n;输出 P[n]。( Pillai's arithmetic function )

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <vector>
#include <cstring>
#include <cmath>
using namespace std;
long long v[150]; //1e11
const int N = 320000;
bool vis[N + 1];
int p[27610], cnt_p; //存素数  27608
int getPrime(int n) {
	memset(vis, 1, sizeof vis);
	int cnt = 0;
	for (int i = 2; i <= n; ++i) {
		if (vis[i]) 	p[cnt++] = i;
		for (int j = 0; j < cnt && (i * p[j] <= n); ++j) {
			vis[i * p[j]] = 0;
			if (i % p[j] == 0) break;
		}
	}
	return cnt;
}
vector <long long> work(long long num) {
	vector<long long> vec;
	for (int i = 0; i < cnt_p; ++i) {
		if (num % p[i] == 0) {
			vec.push_back(p[i]);
			while (num % p[i] == 0) num /= p[i];
		}
		if (num <= 1) break;
	} //得到了素因数
	if (num > 1) vec.push_back(num);
	return vec;
}
long long phi(long long n) {
	long long res = n;
	vector<long long> v = work(n);
	for (int i = 0; i < v.size(); ++i) {
		res = res / v[i] * (v[i] - 1);
	}
	return res;
}
int main()
{
	cnt_p = getPrime(N);
	//cout << phi(37) << endl;
	long long n;
	while (~scanf("%lld", &n)) {
		if (n <= 1) { printf("%lld\n", n); continue; }
		long long m = sqrt(n + 0.5), sz = 0;
		v[sz++] = 1;
		for (int i = 2; i <= m; ++i) {
			if (n % i == 0) {
				v[sz++] = i;//存因数
				v[sz++] = n / i;
			}
		}
		if (m * m == n) sz--; if (n > 1) v[sz++] = n;
		long long res = 0;
		for (int i = 0; i < sz; ++i) {
			res += v[i] * phi(n / v[i]);
		}
		printf("%lld\n", res);
	}

	return 0;
}


河南省2017多校联萌五

上面介绍的是A题。

B题是调和级数求和。当n不大时,直接用定义,当n足够大时,精度变化就不大了,直接用近似算法:

sum(n) = log`` (n + 0.5) + phi; // ``const double phi = 0.57721566490153286060651209;

C题二分,注意结果要求尽量小,所以结果只可能以5或0结尾。

H题,最小生成树。prim算法或者kruskal算法。我用的kruskal算法。

D题过的人也不少,我卡死在了A题。因为质因数筛选时边界问题没处理好。打饭排队时想到的bug.

\