Problem Description
给出一些数字,对于每个数字找到一个欧拉函数值大于等于这个数的数,求找到的所有数的最小和。f(n)的n至少从2开始。
思路:
欧拉函数f(n)定义:表示小于或等于n的数中与n互质的数的数目,f(n) = n∏(1 - 1/p)其中p|n
欧拉函数求值的方法:
(1) f(1) = 1;
(2) 若n是素数p的k次幂,f(n) = p^k - p^(k-1) = (p - 1)*p^(k - 1);
(3) 若m, n互质,f(n*m) = f(n) * f(m);
n为奇数时,f(2*n) = f(2) * f(n) = f(n)。n为质素时,f(n) = n - 1。
欧拉定理。设gcd(a, m) = 1, 则a^(f(m)) = 1(mod)m。费马小定理。对任意a和任意质数p有,a^p ≡ a(mod)p。当gcd(a, p)=1时,由欧拉定理进一步得到a^(p - 1) ≡ 1(mod)p。
根据欧拉函数的定义,可以推出欧拉函数的递推式:
令p为N的最小质因数,若p^2|N, f(n) = f(n/p) * p; 否则f(n) = f(n / p) * (p - 1);
感性证明下:若 p^2|N, 与 n/p 互质的数 + p 的倍数 和 n 也是互质的;否则 gcd(n/p, p) = 1;这题就是打一个欧拉函数的表,然后跑就可以了,所以主要用到递推式
#include<bits/stdc++.h>
using namespace std;
const int N = 1111110;
int minDiv[N], phi[N];//minDiv[i]用来存i的最小质因数,phi[i]就是欧拉函数
int a[10055];
void genPhi()//打欧拉函数表
{
for(int i = 1; i < N; i++){//初始化
minDiv[i] = i;
}
for(int i = 2; i*i < N; i++)
{
if(minDiv[i] == i){//i为质素
for(int j = i*i; j < N; j += i){//更新后面的j的最小质因数
minDiv[j] = i;
}
}
}
phi[1] = 1;//1的欧拉函数为1
for(int i = 2; i < N; i++)
{
phi[i] = phi[i / minDiv[i]];
if((i / minDiv[i]) % minDiv[i] == 0){//minDiv[i]^2|N
phi[i] *= minDiv[i];
}else{
phi[i] *= minDiv[i] - 1;
}
}
}
int main()
{
int T, Case = 1, n, i, x;
genPhi();
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
for(i = 0; i < n; i++)
scanf("%d", &a[i]);
long long ans = 0;
for(i = 0; i < n; i++)
{
x = a[i] + 1;//i肯定大于phi[i]所以,直接从a[i]的后一位开始找
while(phi[x] < a[i]){
x++;
}
ans += (long long)x;
}
printf("Case %d: %lld Xukha\n", Case++, ans);
}
return 0;
}
使用欧拉函数f(n)定义:表示小于或等于n的数中与n互质的数的数目,f(n) = n∏(1 - 1/p)其中p|n 打的表,我们可以在筛素数p的过程中,不断更新素数的倍数x*p*(1-1/p)。这样素数筛完,欧拉函数表也打出来了
#include<bits/stdc++.h>
using namespace std;
const int N = 1111110;
int phi[N];//minDiv[i]用来存i的最小质因数,phi[i]就是欧拉函数
int a[10055];
void genPhi()//打欧拉函数表
{
int i, j;
for(i = 0; i < N; i++)
phi[i] = i;
for(i = 2; i < N; i++)
{
if(phi[i] == i)
{
for(j = i; j < N; j += i)
phi[j] = phi[j]/i * (i - 1);//先除在乘
}
}
}
int main()
{
int T, Case = 1, n, i, x;
genPhi();
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
for(i = 0; i < n; i++)
scanf("%d", &a[i]);
long long ans = 0;
for(i = 0; i < n; i++)
{
x = a[i] + 1;//i肯定大于phi[i]所以,直接从a[i]的后一位开始找
while(phi[x] < a[i]){
x++;
}
ans += (long long)x;
}
printf("Case %d: %lld Xukha\n", Case++, ans);
}
return 0;
}