ACM:数论专题(5)——欧拉函数

  题目描述:
     定义函数φ(k)表示数字1...(k-1)中,和k互质的数字的个数。要求给定区间[L, R],找出在[L,R]范围内, φ(k)值最小的数字,如果有多个数字存在最小值,那么输出数值最小的一个。

解答:
·定理:   
    题目中定义的函数
φ(k)称为“欧拉函数”,该函数具有如下的几个性质:
    定理1:若:k为素数,那么:
φ(k) = k-1
    这个结论是显然成立的,因为k是素数,所以对于k而言,它的约数只有1个,就是1,因此,1...(k-1)的所有数字和k的公约数也只有1,因此,1...(k-1)的数字和k都是互质的,
φ(k) = k-1
    
    定理2:若:p是素数,且n = p^k,那么
φ(n) = (p-1)*p^(k-1)
    首先,所有p的倍数一定和n有一个公约数p,因此p的倍数和n一定不是互质的,而1...(n-1)的范围内,共有 p^(k-1)-1 个p的倍数:p, 2p, 3p, 4p, .... [p^(k-1)-1]*p,因此,1...(n-1)范围内至少有:p^(k-1)-1
 个数字和n不互质。
    其次,对于剩余的那些不是p的倍数的数字,假设x和n存在大于1最大公约数a,那么n和x一定可以被a整除,设:n = Aa,此时分别对A和a分解质因数:
    A = p1 * p2 * ... * pm
    a = q1 * q2 * ... * qs
其中:pi 和 qi 都是素数。并且,因为a>1,因此,上式中对于a的分解质因数,一定至少有1项,即s≥1
此时就可以得到n的另一组分解质因数的结果:
    n = p1 * p2 * ... pm * q1 * q2 * qm
而:
    n = p^k = p * p * p * ... * p (k个p相乘) 
也是n的分解质因数的结果。 由于
每个数字的分解质因数的结果是唯一的(这个定理俺不会证),就可以得到所有的qi和p是相等的,因此a也是p的倍数。
    而a一定可以整除x的,因此x也是p的倍数。这个我们对于“x不是p的倍数”的约定是矛盾的,因此,x一定和n是互质的。
    综上所述:所有的不是p的倍数的数字和p^k一定是互质的。
    1...(n-1)范围内,共有 p^k-1 个数字,p的倍数有p^(k-1)-1个,不是p的倍数的数字共有:
p^k - 1 -   p^(k-1)-1 = p^k - p^(k-1) = (p-1)*p^(k-1)个。因此: φ(n) = (p-1)*p^(k-1),证毕。
    
    定理3:若:p, q互质,那么:
φ(p*q) =  φ(p) *  φ(q)
    要证明这条性质,首先需要证明两条引理: 
   
 引理1: p是素数,那么对于任意的数字u,如果p和u%p互质,那么p和u就互质。
    可以采用反正法进行证明:设: u = mp + r, 其中:r = u%p。
    假设p和u存在大于1的公约数a,那么设:p = xa, u = ya, 那么:ya = m*xa + r => r = (y-mx)a。显然r也是可以被a整除的,那么p和r就拥有大于1的公约数a,此时p和a不互质,这和之前的约定:p和u%p互质是相矛盾的,因此,原结论成立,证毕。

    
引理2:p,q是素数,那么如果一个数字x和p互质,并且x和q也互质,那么x和p*q一定也互质。
    假设x和p*q的公约数是G,设:p*q = AG,x=BG,此时分别对A,B,G,进行分解质因数:
            A = a1 * a2 * ... * am
            B = b1 * b2 * ... * bn
            G = g1 * g2 * ... * gr
其中:ai, bi, gi都是素数,此时可以得到p*q和x的分解质因数的结果:
            p*q = 
a 1 * a2 * ... * a m *  g1 * g2 * ... * gr
            x = 
b1 * b2 * ... * bn  *  g1 * g2 * ... * gr
由于x和p,以及q都互质,因此,x一定不是p和q的倍数,因此,b1, b2 ... bn 以及 g1, g2, ... gr中的任何一个数字都不等于p并且也不等于q。
    又因为任何数字的分解质因数的结果是唯一的,而:p*q = 
a 1 * a2 * ... * a m *  g1 * g2 * ... * gr, 因此:a1, a2, ... am, g1, g2, ... gr 中一定可以找出两项,其值为p或q,而 g1, g2, ... gr中不包含p,q,那么p和q就一定在 a1, a2, ... am中,那么:保证等式: p*q =  a 1 * a2 * ... * a m *  g1 * g2 * ... * gr 成立的条件就是: g1 * g2 * ... * gr = 1, 即:G=1。
    因此:x和p*q的最大公约数就是1,x和p*q是互质的,证毕。 
    对于任意的0...(p*q-1)范围内的数字u,我们可以将其表示为: u = aq + r, 其中:a = u/q, r=u%q,那么:0≤r≤q,由于u         0*q+0,       0*q+1,     0*q+2   ......    0*q+(q-1)
        1*q+0,       1*q+1,     1*q+2   ......    1*q+(q-1)
       
 2*q+0,       2*q+1,     2*q+2   ......    2*q+(q-1)
                         ......
        (p-1)*q+0, (p-1)*q+1, (p-1)*q+2  ...... (p-1)*q+(q-1)
此时可以得到这样的结论:
    
结论1:取出数表中的任意一列,在取出该一行中的任意两个数字做差,结果一定和p互质。 

    证明如下:
       取出数表中任意一列的数字,如下:
            0*q+r, 1*q+r, 2*q+r .... (p-1)*q+r 
    从这些数字中任意取出两个不同的数字:u1 = a1*q + r, u2 = a2*q + r, 做差,取绝对值得:
            |u1-u2| = |a1-a2|*q 
    因为p是素数,因此p和|a1-a2|一定是互质的,同时p和q也是互质的,根据引理2,就可以得到p和|u1-u2|是互质的。证毕。
    得到结论1后,我们可以进一步得到下面的结论:
    
结论2:取出数表中任意一列的数字,如果将其中每一个数字对p取模,那么其结果可以取遍数值:0...(p-1)的所有值。
    证明如下:取出数表中任意一列的数字,对其中每一个数字对p取模可以得到序列:
            R_0, R_1, R_2, ... R_q-1
假设其中有两个数字相同:R_i = R_j, 并设其对应原数表中的数字为Xi, Xj, 并设:
        Xi = A*p + R_i
        Xj = B*p + R_j
此时,做差,去绝对值可得:
        |Xi-Xj| = (A-B)*p
它是p的倍数,因此和p是不互质的。

    而根据结论1,每一行中取出任意两个数字做差,结果一定和p是互质的,此时出现矛盾。因此,序列:
R_0 , R_1, R_2,  ... R_q-1的任意两个数都是不同的,而对p取模只能得到:0, 1, ... , p-1 共p种不同的结果,而序列: R_0 , R_1, R_2,  ... R_q-1也刚好有p个数字,因此序列: R_0 , R_1, R_2,  ... R_q-1就可以取遍数值:0 ... (p-1)中的所有数值。证毕。
    得到结论2后,我们考虑这样一个问题:序列:
 R_0, R_1, R_2, ... R_q-1 中有多少个数字和p互质?显然答案就是 φ(p),再结合引理1,就可以知道:
    
结论3:数表中的每一列,都可以找到
φ(p)个数字和p互质
    最后考虑这样一个问题:如果对数表中的所有数字对q取模,那么:
        第一列的数字的计算结果都是0
        第二列的数字的计算结果都是1
        ......
        第(q-1)列的数字的计算结果都是q-1
而这些结果有多少和q是互质的呢?显然:答案是
φ(q), 再结合引理1,就可以得到:
    
结论4:数表中存在
φ(q)列,这些列中的所有数字都和q互质。
    结合结论3和结论4,我们就会发现:数表中共有
φ(p)* φ(q)个数字和p,q都是互质的。根据引理2,就可以得到:数表中共有 φ(p)* φ(q)个数字和p*q互质,即: φ(p*q)= φ(p)* φ(q)。性质3得证。

·求解:
    根据上面的定理,可以得到一条对于解答本题目很有帮助的一条推论:
    推论1:p是素数,对于任意的数字n,
φ(n*p)的值的求解公式如下:
        若p不可以整除n,那么
φ(n*p) =  φ(n)* φ(p)
        若p可以整除n,那么:
φ(n*p) =  φ(n)*p    
    证明如下:
    可以对n进行分解质因数:n = p1 * p2 * ... * pm,此时n*p的分解质因数的结果就是:
            n*p = 
p1 * p2 * ... * pm * p
式子右边的每一项都是素数,显然,任意两个素数都是互质的,因此如果:p1, p2, ... pm 中的没有一个数字等于p,那么结合引理2,p一定就和n是互质的,此时n一定不能被p整除,此时根据定理3,就可以得到:
φ(n*p) =  φ(n)* φ(p)
    如果 
p1, p2, ... pm 中有k项和p是相同的,此时n是p的倍数,p可以整除n。单独将这k想抽取出来,剩余的数字的乘积记为n',那么:n = n' * p^k, n*p = n' * p^(k+1) 此时,可以知道:n'和p^k, p^(k+1)一定都是互质的,根据定理3,可得:
    
  φ(n) =  φ(n') *  φ(p^k) =    φ(n') * (p-1)*p^(k-1)
    
  φ(n*p) =  φ(n' * p^(k+1)) =  φ(n') *    φ(p^(k+1)) =    φ(n') * (p-1)*p^k
             = 
φ(n') * (p-1)*p^(k-1) * p
因此:
     
φ(n*p) =  φ(n) * p
证毕。

    根据推论1,我们可以构建出一种求解本题的方案:给定数据范围[L, R]后,从2开始枚举n,直到R,对于每一个数值n,做如下的处理:枚举所有小于n的素数,判定每一个素数p是否可以整除n,再根据不同的情况依照推论1进行求解
φ(n*p), 然后枚举n+1,做上述同样的处理。
    简要说明以上步骤的正确性:
    对于每一轮迭代的n值,如果n是素数,那么
φ(n) = n-1,然后就可以求解 φ(n*p)了,而如果n不是素数,那么n一定可以拆分为一个素数和另一个数字的乘积:n = p * x,其中p是素数,而x一定小于n。而通过选择p的值,一定可以找到一个使得p≤x的p*x组合,因为:如果p>x,那么将x分解质因数后,一定有一个小于x的素因子p'(如果x分解质因数结果的每一项都大于p,那么x一定大于p),此时将p'和p对调即可。而x φ(x*p)一定会在本轮迭代之前的迭代算出。因此,每一轮迭代时, φ(n)一定是已知的,因此后续的 φ(n*p)的计算步骤也一定可以顺利进行。
    根据以上的说明,任意一个非素数的数字n,都可以拆分为一个素数p和另一个数字x的乘积,并且可以保证p≤x,因此[L, R]范围内的每一个数字对应的
φ(n)值也一定会在某一轮迭代中求出。
    
    利用上述的步骤,我们就可以求出[L, R]范围内的所有数字对应的
φ(n)值,依次遍历每一个数字,找到 φ(n)值取最小值的最小的那个数字就是本题的答案。

输入输出格式:
    输入: 第1行:2个正整数L, R, 表示给定区间的左右边界
    输出:
1个整数,表示题目要求的答案。

数据范围:
    
2 ≤ L ≤ R ≤5,000,000  

程序代码: 
/****************************************************/
/* File        : Hiho_Week_96                       */
/* Author      : Zhang Yufei                        */
/* Date        : 2016-05-12                         */
/* Description : HihoCoder ACM program. (submit:g++)*/
/****************************************************/

#include
#include
#define NUM 5000000
#define PRIME_NUM 348513

/*
 * The main program.
 */
int main(void) {
	int L, R;
	scanf("%d %d", &L, &R);
	
	int phi[NUM + 1] = {0};
	int prime[PRIME_NUM];
	int prime_cnt = 0;

	int min, min_num;
	min = -1;
		
	for(int i = 2; i < NUM + 1; i++) {
		if(phi[i] == 0) {
			phi[i] = i - 1;
			prime[prime_cnt++] = i;	
			
			if(i >= L && i <= R) {
				if(min == -1 || min > phi[i] || 
				min == phi[i] && min_num > i) {
					min = phi[i];
					min_num = i;
				}
			}
		}
			
		for(int j = 0; j < prime_cnt; j++) {
			if(i * prime[j] > NUM) {
				break;
			}
			if(i % prime[j] == 0) {
				phi[i * prime[j]] = phi[i] * prime[j];
			} else {
				phi[i * prime[j]] = phi[i] * phi[prime[j]];
			}
			
			if(i * prime[j] >= L && i * prime[j] <= R) {
				if(min == -1 || min > phi[i * prime[j]] ||
				min == phi[i * prime[j]] && min_num > i * prime[j]) {
					min = phi[i * prime[j]];
					min_num = i * prime[j];
				}
			}
		}	
	}
	
	printf("%d\n", min_num);
	
	return 0;
}


你可能感兴趣的:(ACM)