#include
using namespace std;
int main()
{
int n,a;
scanf("%d",&n);
while(n--){
scanf("%d",&a);
int res = a;
for(int i=2;i<=a/i;i++){
if(a%i==0){
res = res/i*(i-1);//防止产生小数
while(a%i==0)a /= i;
}
}
if(a>1) res = res/a*(a-1);
printf("%d\n",res);
}
return 0;
}
筛法求欧拉函数,是在质数的线性筛法基础上进行改进的,
代码:
#include
using namespace std;
const int N = 1e6+10;
int primes[N],cnt,phi[N];
bool st[N];
long long get_oula(int n){
phi[1] = 1;
for(int i=2;i<=n;i++){
//当i为质数时,1到i-1都与i互质
if(!st[i])primes[cnt++] = i,phi[i] = i-1;
for(int j=0;primes[j]<=n/i;j++){
st[primes[j]*i] = true;
if(i%primes[j]==0){
/*******
当i%primes[j]==0时,primes[j]是i的最小质因数,
primes[j]*i与i的质因数相同,因此,欧拉函数仅仅多乘了
primes[j]
********/
phi[primes[j]*i] = phi[i]*primes[j];
break;
}
/******
当i%primes[j]!=0时,primes[j]是primes[j]的最小质因数,但
不是i的质因数,而primes[j]*i的其他质因数与i相同,因此
primes[j]*i的欧拉函数比phi[i]多乘了primes[j]*(1-1/primes[j])
******/
phi[primes[j]*i] = phi[i]*(primes[j]-1);
}
}
long long res = 0;
for(int i=1;i<=n;i++)res += phi[i];
return res;
}
int main()
{
int n;
scanf("%d",&n);
long long res = get_oula(n);
printf("%lld",res);
return 0;
}
欧拉定理:如果a与n互质,则 a ϕ ( n ) = 1 ( m o d n ) a^{\phi(n)}=1(mod \ n) aϕ(n)=1(mod n)
如果p为质数,则 a p − 1 = 1 ( m o d p ) a^{p-1}=1(mod\ p) ap−1=1(mod p).
快速幂可以在O(logk)的时间复杂度内求出 a k m o d p a^k mod \ p akmod p
快速幂的步骤:
#include
using namespace std;
typedef long long LL;
LL a,b,p,n;//防止溢出,全部用long long
int main()
{
scanf("%lld",&n);
while(n--){
scanf("%lld%lld%lld",&a,&b,&p);
LL res = 1;
while(b){//一边乘,一边预处理,用数组预处理好像不太对,我太菜了不会写。
if(b&1)res = res*a%p;
b = b>>1;
a = a*a%p;
}
printf("%lld\n",res);
}
return 0;
}
逆元定义:若整数 b,m 互质,并且对于任意的整数 a,如果满足 b|a,则存在一个整数 x,使得 a/b≡a×x(modm),则称 x 为 b 的模 m 乘法逆元,记为 b − 1 ( m o d m ) b^{−1}(mod\ m) b−1(mod m)。
b 存在乘法逆元的充要条件是 b 与模数 m 互质。当模数 m 为质数时, b m − 2 b^{m−2} bm−2 即为 b 的乘法逆元。
逆元的作用就是将除法转换成乘法。
#include
using namespace std;
typedef long long LL;
LL n,a,p;
LL qmi(LL a,LL b,LL p){
LL res = 1;
while(b){
if(b&1)res = res*a%p;
b = b>>1;
a = a*a%p;
}
return res;
}
int main()
{
scanf("%lld",&n);
while(n--){
scanf("%lld%lld",&a,&p);
if(a%p==0)printf("impossible\n");
else{
LL res = qmi(a,p-2,p);
printf("%lld\n",res);
}
}
return 0;
}
裴蜀定理:若a,b是整数,且gcd(a,b)=d,那么对于任意的整数x,y,ax+by=m中的m一定是d的倍数。(特别地,如果a、b是整数,那么一定存在整数x、y使得ax+by=gcd(a,b)。)
在欧几里得算法中:
int gcd(int a,int b){
if(!b){
return a;
}
return gcd(b,a%b);
}
就是说当b=0时,a中存的就是最大公约数,则当b=0时,一定有: a × 1 + b × 0 = g c d ( a , b ) a×1+b×0=gcd(a,b) a×1+b×0=gcd(a,b),此时x = 1,y = 0;
而当b!=0时:
假设之前有 a × x 1 + b × y 1 = g c d a×x_1+b×y_1=gcd a×x1+b×y1=gcd
下一步计算gcd(b,a%b)时有 b × x 2 + ( a % b ) × y 2 = g c d b×x_2+(a\%b)×y_2=gcd b×x2+(a%b)×y2=gcd
所以, a × x 1 + b × y 1 = b × x 2 + ( a % b ) × y 2 a×x_1+b×y_1 = b×x_2+(a\%b)×y_2 a×x1+b×y1=b×x2+(a%b)×y2,a%b = a-(a/b)*b;
a x 1 + b y 1 = b x 2 + a y 2 − ( a / b ) ∗ b y 2 ax_1+by_1=bx_2+ay_2-(a/b)*by_2 ax1+by1=bx2+ay2−(a/b)∗by2
所以, x 1 = y 2 , y 1 = x 2 − ( a / b ) ∗ y 2 x_1=y_2,y1=x2-(a/b)*y_2 x1=y2,y1=x2−(a/b)∗y2
扩展欧几里得算法如下:
int exgcd(int a,int b,int& x,int& y){
if(!b){
x = 1;
y = 1;
return a;
}
int d = exgcd(b,a%b,x,y);
int temp = x;
x = y;
y = temp - (a/b)*y;
return d;
}
给定 n 组数据 a i , b i , m i a_i,b_i,m_i ai,bi,mi,对于每组数求出一个 x i x_i xi,使其满足 a i × x i ≡ b i ( m o d m i ) a_i×x_i≡b_i(mod\ m_i) ai×xi≡bi(mod mi)
也就是 a x + m y = b ax+my=b ax+my=b,可以用扩展欧几里得算法求x和y,只有b是gcd(a,m)的倍数时有解,最后x扩大(b/d)就是解。
#include
using namespace std;
int a,b,m;
int n;
int exgcd(int a,int b,int& x,int &y){
if(!b){
x = 1;
y = 0;
return a;
}
int d = exgcd(b,a%b,x,y);
int temp = x;
x = y;
y = temp - (a/b)*y;
return d;
}
int main()
{
scanf("%d",&n);
while(n--){
scanf("%d%d%d",&a,&b,&m);
int x,y;
int d = exgcd(a,m,x,y);
if(b%d)printf("impossible\n");
else printf("%lld\n",(long long)x*(b/d)%m);
}
return 0;
}