题目描述:
给定 a,b,求 1≤x<a^b 中有多少个 x 与 a^b 互质。
由于答案可能很大,你只需要输出答案对 998244353 取模的结果。
输入格式:
输入一行包含两个整数分别表示 a,b,用一个空格分隔。
输出格式:
输出一行包含一个整数表示答案。
数据范围:
对于 30% 的评测用例,ab≤1e6;
 对于 70% 的评测用例,a≤1e6,b≤1e9;
 对于所有评测用例,1≤a≤1e9,1≤b≤1e18。
输入样例1:
2 5
输出样例1:
16
输入样例2:
12 7
输出样例2:
11943936分析步骤:
第一:我们看到这个题目,要求有多少个数互质。首先,就应该想到我们肯定是要用欧拉函数的解题目的;其次,我们看到有a的b次方这种指数运算的话,就应该想到要运用快速幂的算法,特别是像这种题目数据量这么大的情况,一定一定是要用快速幂的。所以此题目分析到这里我们已经把题目的两大特点分析出来了:1.快速幂;2.欧拉函数。
第二:回顾欧拉函数:
-  我们先简单回顾一下什么是欧拉函数。
-  欧拉函数的定义:1~n中与n互质的数的个数成为欧拉函数
-  欧拉函数的性质:
-  如果p是质数的话,则与p互质的个数有:p-1个
-  如果p是质数的话,则与p^k互质的个数有:(p-1)*p^(k-1)
-  积性函数:若gcd(m,n) = 1也就是最大公约数为1时,则与(m*n)互质的个数有:与m的互质的个数乘上与n的互质的个数,就是(m*n)的答案。
-  欧拉函数的计算公式: 
第三:书写主函数,构建整体框架:
-  我们这道题目数据量巨大,所以我们最好定义a,b为long long类型
-  如果底数 a == 1 的话那么无论b是多少答案都必定是0,这是一种剪枝的操作。
-  进入我们欧拉函数的while循环
-  我们运用试除法求因数,判断如果 x % i == 0 的话就代表 i 是 x 的因数,那么我们就把这个值给除干净;那么我们在让res乘上他的贡献。
-  while退出之后,在判断一下x是不是1,如果不是1的话就代表这个数也是一个因数,再让res乘上这个数的贡献。
-  最后我们只需要把res的乘上快速幂的值那么我们的答案就出来了。
int main()
{
    LL a, b;
    cin >> a >> b;
    if (a == 1)
    {
        cout << 0 << endl;
        return 0;
    }
    LL res = a, x = a;
    for (int i = 2; i * i <= x; i ++ )
        if (x % i == 0)
        {
            while (x % i == 0) x /= i;
            res = res / i * (i - 1);
        }
    if (x > 1) res = res / x * (x - 1);
    cout << res * qmi(a, b - 1) % MOD << endl;
    return 0;
}第四:书写快速幂函数:
LL qmi(LL a, LL b)
{
    LL res = 1;
    while (b)
    {
        if (b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return res;
}
----------
第五:线性筛欧拉函数:
-  我们线性筛欧拉函数,可以在O(logn)的时间复杂度之内完成筛法,大大降低了时间复杂度,比试除法更快更好
-  线性筛欧拉函数也是借用线性筛模板,大同小异。
-  首先我们定义st数组来判断我们这个数是不是质数,定义primes数组储存质数,定义mem数组储存我们之前算出的每一个数的答案。相当于记忆化搜索。
-  将mem[1]初始化为1,因为1的质数只有1个。
-  进入for循环,如果这个数状态没有被改过那么就证明这个数是质数,我们就把这个数放入质数数组,并且因为这个数是质数,那么这个数从1~i的与其互质的数为i-1。
-   
-  再进入一个for循环,我们要保证每一个合数一定是被他最小的质因子给筛去(这是线性筛的精髓)所以我们将i乘primes[j]赋给m,更改m的状态。因为m一定是两个数的乘积之和,那么他一定是合数所以要改变状态。
-  再判断这个质因子是不是i的因子。如果是的话则代表了,i包含了m的所有质因子.例如:(12) = (2 * 6),因为2是质数,那么他就可以直接乘,6我们在之前的记忆化计算之中已经算出来了。所以可以写成 mem[m] = primes[j] * mem[i];
-  如果不是的话,则代表i不能被primes[j]整除,则i与primes[j]互质,因为primes[j]一定是质数,所以他的互质数就等于primes[j] - 1,这是欧拉函数的性质,i的互质数就在我们之前记忆化搜索之中也计算出来了。将两个数相乘就可以得出答案。mem[m] = (primes[j]-1) * mem[i];
bool st[N];
int primes[N];
int mem[N];
void get_prime(int n ){
    mem[1] = 1;
    for(int i = 2 ; i <= n ; i ++){
        if(!st[i]) {
            primes[cnt++] = i;
            mem[i] = i-1;
        }
        for(int j = 0 ; i * primes[j] <= n ; j++){
            int m = i * primes[j];
            st[m] = true;
            if(i % primes[j] == 0) {
                mem[m] = primes[j] * mem[i];
                break;
            }else{
                mem[m] = (primes[j]-1) * mem[i];
            }
        }
    }
}----------
大家可以好好看看如何用线性筛求解欧拉函数!!!这很重要,线性筛可以节约很多时间。
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MOD = 998244353;
LL qmi(LL a, LL b)
{
    LL res = 1;
    while (b)
    {
        if (b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return res;
}
int main()
{
    LL a, b;
    cin >> a >> b;
    if (a == 1)
    {
        cout << 0 << endl;
        return 0;
    }
    LL res = a, x = a;
    for (int i = 2; i * i <= x; i ++ )
        if (x % i == 0)
        {
            while (x % i == 0) x /= i;
            res = res / i * (i - 1);
        }
    if (x > 1) res = res / x * (x - 1);
    cout << res * qmi(a, b - 1) % MOD << endl;
    return 0;
}



















