zoukankan      html  css  js  c++  java
  • 关于一道7倍经验的欧拉函数题

    这篇博客将会介绍一道7倍经验的欧拉函数题。

    update:欧拉函数的求法

    根据式子求:

    int phi(int n) {
    	int ret = n;
    	for (int i = 1; prime[i] * prime[i] <= n; i++) {
    		if (n % prime[i] == 0) {
    			ret /= prime[i];
    			ret *= (prime[i] - 1);
    			while (n % prime[i] == 0)  n /= prime[i];
    		}
    	}
    	if (n > 1) {
    		ret /= n;
    		ret *= (n - 1);
    	}
    	return ret;
    } 
    

    埃式筛法:

    void init() {
    	for (int i = 1; i <= MAX; i++) {
    		phi[i] = i;
    	}
    	for (int i = 2; i <= MAX; i++) {
    		if (phi[i] == i) {
    			for (int j = i; j <= MAX; j += i) {
    				phi[j] /= i;
    				phi[j] *= (i - 1);
    			}
    			phi[i] = i - 1;
    		}
    	}
    }
    

    根据欧拉函数的性质可以得出其线性筛的代码:

    void init() {
    	phi[1] = 1;
    	for (int i = 2; i <= MAX; i++) {
    		if (!mark[i]) {
    			prime[++m] = i;
    			phi[i] = i - 1;
    		}
    		for (int j = 1; j <= m && prime[j] * i <= MAX; j++) {
    			phi[i * prime[j]] = phi[i] * (i % prime[j] ? (prime[j] - 1) : prime[j]);
                mark[i * prime[j]] = 1;
    			if (i % prime[j] == 0) break;
    		}
    	}
    }
    

    基础题

    UVA11417 GCD

    这应该是这7道题中最水的一道,因为它的范围只有 (1 < N < 501)

    如果你会欧几里得算法你应该就能A这道题,暴力枚举两个数,求其 (gcd),时间复杂度 (O(TN^2log{N}))

    这个暴力的代码我就不给了。

    P1390 公约数的和

    这题题面说的有点迷,其实和上一题求的是一个东西。只不过数据范围变大了且没有了多组数据。

    对要求的东西进行分析:设 (f(n)=sumlimits_{i=1}^{n-1}gcd(i,n)),则题目要求即为 (sumlimits_{i=2}^{n}f(i))

    发现如果 (gcd(i,j)=1),则 (gcd(ik,jk)=k)

    我们可以枚举最大公约数 (k),然后(f(n)=sumlimits_{k|n}k imes phi(k))(phi)是欧拉函数)。

    通过提前将欧拉函数筛出来可以(O(1))查询欧拉函数,但这样做的复杂度是(O(Nsqrt{N})),还是太慢。

    我们考虑枚举公约数的时候把所有是其倍数的(n)都算出来,这样的(n)其实就是(k,2k,3k, cdots, floor(frac{N}{k}) imes k),答案会加上(k imes sumlimits_{i=1}^{floor(frac{N}{k})}phi(i)),所以我们求一个欧拉函数的前缀和,这样对于每一个(gcd:k)就能做到(O(1))了,总复杂度(O(N))

    注意这道题((a,a))这种不算,所以在计算时(phi(1))不算,要减掉。

    #include <iostream>
    #include <cstdio>
    using namespace std;
    const int N = 2000010;
    long long prime[N], phi[N], tot;
    long long sum[N];
    bool mark[N];
    long long n;
    long long ans;
    void get_phi() {
    	phi[1] = sum[1] = 1;
    	for (int i = 2; i <= n; i++) {
    		if (!mark[i]) {
    			prime[++tot] = i;
    			phi[i] = i - 1;
    		}
    		for (int j = 1; j <= tot; j++) {
    			if (i * prime[j] > n) break;
    			mark[i * prime[j]] = 1;
    			phi[i * prime[j]] = phi[i] * ((i % prime[j] == 0) ? prime[j] : (prime[j] - 1));
    			if (i % prime[j] == 0) break;
    		}
    		sum[i] = sum[i - 1] + phi[i];
    	}
    }
    int main() {
    	cin >> n;
    	get_phi();
    	for (int i = 1; i <= n; i++) {
    		ans += (sum[n / i] - 1) * i;
    	}
    	cout << ans;
    	return 0;
    }
    

    UVA11424 GCD - Extreme (I)

    这道题多了个多组数据,如果还按上述做法做的话复杂度是(O(TN))会炸。

    考虑提前预处理答案,做到(O(1))查询。

    根据上面的推到,可以设计一个类似埃式筛法的算法,枚举所有公约数(i),把他的倍数(j)的答案都加上(i imes Phi(frac{j}{i})),当然(j eq k)

    这只是算出了(f(n)),还要做一遍前缀和就是题目所求。

    这个算法的复杂度是(O(Nlog{N}+T))的,在解决多组数据的问题时胜过上述算法。

    #include <iostream>
    #include <cstdio>
    using namespace std;
    const int N = 1000010;
    long long prime[N], phi[N], ans[N], tot;
    bool mark[N];
    int n;
    void get_ans() {
    	phi[1] = 1;
    	for (int i = 2; i <= 1000000; i++) {
    		if (!mark[i]) {
    			prime[++tot] = i;
    			phi[i] = i - 1;
    		}
    		for (int j = 1; j <= tot; j++) {
    			if (prime[j] * i > 1000000) break;
    			mark[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);
    			}
    		}
    	}
    	for (int i = 1; i <= 1000000; i++) {
    		for (int j = i + i; j <= 1000000; j += i) {
    			ans[j] += i * (phi[j / i]);
    		}
    	}
    	for (int i = 1; i <= 1000000; i++) {
    		ans[i] += ans[i - 1];
    	}
    }
    int main() {
    	get_ans();
    	while (scanf("%d", &n) != EOF && n) {
    		printf("%lld
    ", ans[n]);
    	}
    	return 0;
    }
    

    UVA11426 拿行李(极限版) GCD - Extreme (II)

    SP3871 GCDEX - GCD Extreme

    这两题和前一题基本一样,数据范围并没有太大的变化,用前面的代码就可以A。

    变式

    P2398 GCD SUM

    这题乘个(2)再把((a,a))这种情况加上就行了。

    P2568 GCD

    这题把枚举所有约数变成枚举所有质数,求和变成求个数即可。

    #include <iostream>
    #include <cstdio>
    using namespace std;
    const int N = 10000010;
    int prime[N], phi[N], tot;
    long long sum[N];
    bool mark[N];
    int n;
    long long ans;
    void get_phi() {
    	phi[1] = sum[1] = 1;
    	for (int i = 2; i <= n; i++) {
    		if (!mark[i]) {
    			prime[++tot] = i;
    			phi[i] = i - 1;
    		}
    		for (int j = 1; j <= tot; j++) {
    			if (i * prime[j] > n) break;
    			mark[i * prime[j]] = 1;
    			phi[i * prime[j]] = phi[i] * ((i % prime[j] == 0) ? prime[j] : (prime[j] - 1));
    			if (i % prime[j] == 0) break;
    		}
    		sum[i] = sum[i - 1] + phi[i];
    	}
    }
    int main() {
    	cin >> n;
    	get_phi();
    	for (int i = 1; i <= tot; i++) {
    		ans += 2 * sum[n / prime[i]] - 1;
    	}
    	cout << ans;
    	return 0;
    }
    

    ❀❀完结撒花❀❀

  • 相关阅读:
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    网上怎么赚钱?如何利用CPA赚钱?
    在农村养殖什么赚钱?要做好哪些预防工作?
    计算机组成的基本硬件设备
    JAVA回溯法求0-1背包
    TensorFlow学习笔记之批归一化:tf.layers.batch_normalization()函数
    数据库连接池的大小值设定,不能随意设置太大的值
    N皇后问题
    19个强大、有趣、又好玩的 Linux 命令!
    JAVA马的遍历
  • 原文地址:https://www.cnblogs.com/zcr-blog/p/14897337.html
Copyright © 2011-2022 走看看