欧拉函数-LightO_1370 Bi-shoe and Phi-shoe

Problem

LightOJ_1370
Bamboo Pole-vault is a massively popular sport in Xzhiland. And Master Phi-shoe is a very popular coach for his success. He needs some bamboos for his students, so he asked his assistant Bi-Shoe to go to the market and buy them. Plenty of Bamboos of all possible integer lengths (yes!) are available in the market. According to Xzhila tradition,

Score of a bamboo = Φ (bamboo’s length)

(Xzhilans are really fond of number theory). For your information, Φ (n) = numbers less than n which are relatively prime (having no common divisor other than 1) to n. So, score of a bamboo of length 9 is 6 as 1, 2, 4, 5, 7, 8 are relatively prime to 9.

The assistant Bi-shoe has to buy one bamboo for each student. As a twist, each pole-vault student of Phi-shoe has a lucky number. Bi-shoe wants to buy bamboos such that each of them gets a bamboo with a score greater than or equal to his/her lucky number. Bi-shoe wants to minimize the total amount of money spent for buying the bamboos. One unit of bamboo costs 1 Xukha. Help him.

Input

Input starts with an integer T (≤ 100), denoting the number of test cases.

Each case starts with a line containing an integer n (1 ≤ n ≤ 10000) denoting the number of students of Phi-shoe. The next line contains n space separated integers denoting the lucky numbers for the students. Each lucky number will lie in the range [1, 106].

Output

For each case, print the case number and the minimum possible money spent for buying the bamboos. See the samples for details.

Sample Input

3
5
1 2 3 4 5
6
10 11 12 13 14 15
2
1 1

Sample Output

Case 1: 22 Xukha
Case 2: 88 Xukha
Case 3: 4 Xukha

题意及解法

题面不短,但介绍的只有两个事情:
1.规定竹子的得分是竹子长度的欧拉函数值,score=euler[length]
2.我们要求得欧拉函数值大于等于每一个幸运数字的最短竹子长度之和。对于某个幸运数字,只需要找到欧拉函数值大于等于他的最短长度就行。

求欧拉函数其实相当简单,这个下面会讲到。就是这个查找,我一开始以为,这个挺难找到的,写了一个ans记录对于每一个长度它选择的欧拉函数值是多少,但MLE了,因为开的太大了,实际上,一个数的欧拉函数最大不会大过他本身(介绍欧拉函数的时候会说到)。后来又想写二分,但实际上,欧拉函数并不是有序的,是乱序的,就意味着,某一n前可能已经存在一个m比它的欧拉函数值大了。最后试了一下直接暴力去找即可ac。

欧拉函数

在数论,对正整数n,欧拉函数是小于n的正整数中与n互质的数的数目。
像这道题里给的例子,在小于等于9的数中,与9互质的有1,2,4,5,7,8一共6个数,所以φ(9)=6。

通式

要计算一个正整数n的欧拉函数的方法如下:
将n表示成素数的乘积: n = p 1 k 1 ∗ p 2 k 2 ∗ . . . ∗ p n k n n = p_1 ^ {k_1} * p_2 ^ {k_2} * ... * p_n ^ {k_n} n=p1k1p2k2...pnkn
p 1 , p 2 , . . . , p n 是 素 数 p_1, p_2, ..., p_n是素数 p1,p2,...,pn我们都知道,任何一个数都可以表示成素数幂乘积的形式(唯一分解定理) 。
欧拉函数通式:
φ ( n ) = ( p 1 k 1 − p 1 k 1 − 1 ) ∗ . . . ∗ ( p n k n − p n k n − 1 ) φ(n) = (p_1 ^{ k_1} - p_1 ^ {k_1 - 1}) * ... * (p_n ^ {k_n} - p_n ^ {k_n - 1}) φ(n)=(p1k1p1k11)...(pnknpnkn1)
= n ∗ ( p 1 − 1 ) ( p 2 − 1 ) … … ( p i − 1 ) / ( p 1 ∗ p 2 ∗ … … p i ) =n*(p_1-1)(p_2-1)……(p_i-1)/(p_1*p_2*……p_i) =n(p11)(p21)(pi1)/(p1p2pi)
= n ∗ ( 1 − 1 p 1 ) ∗ ( 1 − 1 p 2 ) . . . . ( 1 − 1 p n ) =n*(1-\frac{1}{p_1})*(1-\frac{1}{p_2})....(1-\frac{1}{p_n}) =n(1p11)(1p21)....(1pn1)

性质

(1) p^k型欧拉函数:

若N是质数p(即N=p), φ(n)= φ( p)=p-p^(k-1)=p-1。

若N是质数p的k次幂(即N=p^ k),φ(n)=p^ k -p^ (k-1)=(p-1)p^(k-1)。

(2)mn型欧拉函数

设n为正整数,以φ(n)表示不超过n且与n互素的正整数的个数,称为n的欧拉函数值。若m,n互质,φ(mn)=(m-1)(n-1)=φ(m)φ(n)。

(3)特殊性质:

若n为奇数时,φ(2n)=φ(n)。

对于任何两个互质 的正整数a,n(n>2)有:a^φ(n)=1 mod n (恒等于)此公式即 欧拉定理

当n=p 且 a与素数p互质(即:gcd(a,p)=1)则上式有: a^(p-1)=1 mod n (恒等于)此公式即 费马小定理

欧拉函数常见的有几种求法,一种是直接按照公式找,另一种是结合线性筛的运算打表。
详见:https://www.cnblogs.com/handsomecui/p/4755455.html

ac代码

代码1暴力找
其实呢,这种复杂度也不高的,就是从一个质数跑到比他大一点的那个质数。1e6范围内,两个质数间不会有隔得太远的情况,最多跑个几百就到了。

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
#define LL long long
#define MOD 1000000007
#define MAXN 100007
const int INF = 0x3f3f3f3f;
const int N = 1000007;
LL read()
{
	LL w = 1, x = 0;
	char ch = 0;
	while (ch < '0' || ch>'9')
	{
		if (ch == '-')
			w = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9')
	{
		x = x * 10 + (ch - '0');
		ch = getchar();
	}
	return w * x;
}

int euler[N];
int ans[N];
void Init()
{
	euler[1] = 0;
	for (int i = 2; i < N; i++)
	{
		if (!euler[i])
		{
			for (int j = i; j < N; j += i)
			{
				if (!euler[j])
				{
					euler[j] = j;
				}
				euler[j] = euler[j] / i * (i - 1);
			}
		}
	}
}
int main()
{
	Init();
	int T;
	cin >> T;
	for (int t = 1; t <= T; ++t)
	{
		int n;
		cin >> n;
		LL res = 0;
		for (int i = 1; i <= n; ++i)
		{
			int m;
			cin >> m;
			for (LL j = 1LL * m; j < 1000006; ++j)
			{
				if (euler[j] >= m)
				{
					res += j;
					break;
				}
			}
		}
		printf("Case %d: %lld Xukha\n", t, res);
	}
	return 0;
}

代码2:预处理版
还是先求欧拉函数,求完之后,跑两遍O(1e6),第一遍让每个欧拉函数值对应的最小n存在ans数组中。第二遍从后往前让ans变成一个不严格递增的序列。注意最后ans要稍微跑的大一点点,离1e6最近的质数是1e6+3,至少要跑到这里,否则在最后几个值上会出错。

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
#define LL long long
#define MOD 1000000007
#define MAXN 100007
const int INF = 0x3f3f3f3f;
const int N = 1000007;
LL read()
{
	LL w = 1, x = 0;
	char ch = 0;
	while (ch < '0' || ch>'9')
	{
		if (ch == '-')
			w = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9')
	{
		x = x * 10 + (ch - '0');
		ch = getchar();
	}
	return w * x;
}

int euler[N];
int ans[N];
void Init()
{
	euler[1] = 0;
	for (int i = 2; i < N; i++)
	{
		if (!euler[i])
		{
			for (int j = i; j < N; j += i)
			{
				if (!euler[j])
				{
					euler[j] = j;
				}
				euler[j] = euler[j] / i * (i - 1);
			}
		}
	}
}
int main()
{
	Init();
	int minn = INF;

	for (int i = 1; i < N; ++i)
	{
		if (ans[euler[i]] == 0)
		{
			ans[euler[i]] = i;
		}
	}
	for (int i = 1000006; i >=1; --i)
	{
		if (ans[i] == 0)
		{
			ans[i] = minn;
		}
		else
		{
			minn = min(minn, ans[i]);
			ans[i] = minn;
		}
	}
	int T;
	cin >> T;
	for (int t = 1; t <= T; ++t)
	{
		int n;
		cin >> n;
		LL res = 0;
		for (int i = 1; i <= n; ++i)
		{
			int m;
			cin >> m;
			res += ans[m];
		}
		printf("Case %d: %lld Xukha\n", t, res);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值