题目大意

给你一个nn,求最小正整数k,使得对于任意整数aa都满足
anka(mod n)
或输出无解。

做法

首先发现nn含有平方因子时是无解的。
假设n含有平方因子pcpc,那么我们令a=npcpa=npcp时是不合法的。
因为nkn2ccnk≥n≥2c≥c,因此ankank显然模nn为0,而显然ann不为0
考虑中国剩余定理,因此对于nn每个质因子p都要有anka(mod p)ank≡a(mod p)
aap的倍数显然成立,这里只考虑(a,p)=1(a,p)=1
ank11(mod p)ank−1≡1(mod p)
接下来我们可以证明p1|nk1p−1|nk−1
因为pp是质数,一定存在原根g
如果a=ga=g,显然p1|nk1p−1|nk−1
否则设a=gca=gc,显然cc不为0。有c(nk1)0(mod p1)c(nk−1)≡0(mod p−1)
显然也有p1|nk1p−1|nk−1
因此我们现在求出所有p1p−1的最小公倍数,记为lcmlcm,那么lcm|nk1lcm|nk−1
因此nk1(mod lcm)nk≡1(mod lcm)
如果(n,lcm)(n,lcm)不为11显然无解,否则肯定有解,因为ϕ(lcm)就是可行解。
ϕ(lcm)ϕ(lcm)分解质因数,枚举一个质因数并代入检验,即可得到最小kk

#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
ll p[1000];
int c[1000];
ll n,phi,k;
int i,tot,top;
ll random(ll x){
    ll t=rand()%10000;
    t=t*10000+rand()%10000;
    t=t*10000+rand()%10000;
    t=t*10000+rand()%10000;
    return t%x;
}
ll mul(ll a,ll b,ll p){
    ll tmp=(a*b-(ll)((long double)a/p*b+1e-8)*p);
    return tmp<0?tmp+p:(tmp>=p?tmp-p:tmp);
}
ll qsm(ll x,ll y,ll mo){
    if (!y) return 1;
    ll t=qsm(x,y/2,mo);
    t=mul(t,t,mo);
    if (y%2) t=mul(t,x,mo);
    return t;
}
bool Miller_Rabin(ll n){
    if (n==1) return 0;
    int s=10,t=0,i;
    ll a,p,k=n-1;
    while (k%2==0) k/=2,t++;
    while (s--){
        a=random(n-1)+1;
        p=a=qsm(a,k,n);
        fo(i,1,t){
            a=mul(a,a,n);
            if (a==1&&p!=1&&p!=n-1) return 0;
            p=a;
        }
        if (a!=1) return 0;
    }
    return 1;
}
ll gcd(ll a,ll b){
    return b?gcd(b,a%b):a;
}
ll Pollard_Rho(ll n){
    ll k,x,y,c,d,i=1;
    while (1){
        c=random(n-1);
        k=2;y=x=random(n);
        i=1;
        while (1){
            y=(mul(y,y,n)+c)%n;
            d=gcd(abs(x-y),n);
            if (i==k) x=y,k<<=1;
            i++;
            if (d!=1) break;
        }
        if (d!=n) return d;
    }
}
void work(ll n){
    if (Miller_Rabin(n)){
        p[++top]=n;
        return;
    }
    ll p=Pollard_Rho(n);
    ll q=n/p;
    work(p);work(q);
}
void dfs(int x,ll y){
    if (y>=k) return;
    if (x==top+1){
        ll t=qsm(n,y,phi);
        (t+=phi)%=phi;
        if (t==1) k=y;
        return;
    }
    int i;
    ll t=1;
    fo(i,0,c[x]){
        dfs(x+1,y*t);
        t=t*p[x];
    }
}
int main(){
    freopen("pow.in","r",stdin);freopen("pow.out","w",stdout);
    scanf("%lld",&n);
    work(n);
    sort(p+1,p+top+1);
    phi=1;
    fo(i,1,top){
        phi=phi*(p[i]-1)/gcd(phi,p[i]-1);
        if (p[i]==p[i-1]){
            printf("-1\n");
            return 0;
        }
    }
    if (gcd(phi,n)!=1){
        printf("-1\n");
        return 0;
    }
    if (phi==1){
        printf("1\n");
        return 0;
    }
    top=0;
    work(phi);
    sort(p+1,p+top+1);
    k=1;
    fo(i,1,top){
        if (p[i]==p[i-1]) k=k*p[i];
        else k=k*(p[i]-1);
    }
    if (k==1){
        printf("1\n");
        return 0;
    }
    top=0;
    work(k);
    sort(p+1,p+top+1);
    tot=top;
    top=0;
    fo(i,1,tot){
        if (p[i]!=p[i-1]){
            top++;
            p[top]=p[i];
            c[top]=1;
        }
        else c[top]++;
    }
    dfs(1,1);
    printf("%lld\n",k);
}
<think>首先,用户的问题是关于快速算法的思路。用户说:“我想了解快速算法的思路 请问快速算法的思路”。我需要根据提供的引用和系统指令来回答。 回顾系统指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式必须使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如 [^1]。 - 回答结构清晰,帮助用户逐步解决问题,尽量保证回答真实可靠。 用户提供了三个引用: - :快速算法的简单实现,包括代码。 - [^2]:快速算法的思路,从二分法和二进制角度解析。 - [^3]:快速算法的详解,包括代码实现。 用户之前的问题可能是关于快速的,但我没有历史记录。用户说“参考站内引用”,所以我应该基于这些引用构建答案。 关键点:解释快速算法的思路。引用[2]直接提到了思路:从二分法和二进制两个角度解析,将时间复杂度从O(n)降低到O(log n)。 结构化我的回答: 1. **介绍快速算法**:简要描述它是什么,为什么有用。 2. **基本思路**:从简单方法开始(循环相乘),然后引入快速的思路。 3. **详细解释思路**: - 从二分法角度:如何使用分治策略,例如计算 \(x^n\) 时,如果n是偶数,则计算 \((x^{n/2})^2\);如果n是奇数,则计算 \(x \times (x^{(n-1)/2})^2\)。 - 从二进制角度:将指数n表示为二进制,然后基于二进制位进行乘方。 4. **伪代码或代码示例**:参考提供的代码片段。 5. **时间复杂度分析**:解释为什么是O(log n)。 6. **生成相关问题**:在末尾添加相关问题。 确保使用数学表达式: - 行内:如 $x^n$、$O(n)$ 等。 - 独立公式:如定义快速递归式时,使用 $$...$$。 例如: - 基本公式:$$x^n = \begin{cases} (x^{n/2})^2 & \text{if } n \text{ is even} \\ x \times (x^{(n-1)/2})^2 & \text{if } n \text{ is odd} \end{cases}$$ 参考引用: - 提到“二分法”和“二进制”两个角度。 - 和 提供代码实现。 所以,回答结构: 1. **引言**:快速算法用于高效计算运算,特别是大指数情况。 2. **简单方法**:循环相乘,时间复杂度 $O(n)$。 3. **快速思路**: - **二分法角度**:递归或迭代地分解问题。 - **二进制角度**:利用指数的二进制表示,通过位操作累加结果。 4. **示例代码**:展示一个简单的实现。 5. **总结**:时间和空间复杂度。 6. **相关问题**:生成几个相关问题。 最后,生成相关问题,如: 1. 快速算法的时间复杂度是如何推导的? 2. 快速在实际应用中有哪些例子? 3. 如何优化快速算法以处理大数? 确保语言清晰、中文。 </think> ### 快速算法的思路 快速算法是一种高效计算运算(如 $x^n$)的方法,它通过优化计算过程将时间复杂度从简单循环的 $O(n)$ 降低到 $O(\log n)$,特别适用于处理大指数(如密码学或大数计算)。其核心思路基于分治策略和二进制分解,下面我将逐步解释。 #### 1. **简单方法的不足** - 最简单的方法是循环相乘:计算 $x^n$ 时,通过 $n$ 次乘法迭代(例如,$x \times x \times \cdots \times x$)。时间复杂度为 $O(n)$,当 $n$ 很大时(如 $n=10^6$),效率低下[^2]。 #### 2. **快速的核心思路** 快速算法从两个角度解析:**二分法**和**二进制表示**,核心是减少乘法次数。 - **二分法角度(分治策略)**: - 利用运算的性质:如果指数 $n$ 是偶数,可以将问题分解为 $(x^{n/2})^2$;如果 $n$ 是奇数,则分解为 $x \times (x^{(n-1)/2})^2$。 - 递归或迭代地应用这一分解,直到指数为 0 或 1。 - 数学表示为: $$ x^n = \begin{cases} 1 & \text{if } n = 0 \\ x & \text{if } n = 1 \\ (x^{n/2})^2 & \text{if } n \text{ is even} \\ x \times (x^{(n-1)/2})^2 & \text{if } n \text{ is odd} \end{cases} $$ - 例如,计算 $2^8$($n=8$ 是偶数): - $2^8 = (2^4)^2 = ((2^2)^2)^2 = ((4)^2)^2 = 16^2 = 256$,仅需 3 次乘法(vs. 7 次循环)。 - 这减少了乘法次数,时间复杂度降为 $O(\log n)$[^2]。 - **二进制角度(位运算优化)**: - 将指数 $n$ 表示为二进制形式(例如,$n = b_k b_{k-1} \cdots b_0$,其中 $b_i$ 是二进制位)。 - 运算可以分解为:$x^n = x^{\sum_{i=0}^{k} b_i \cdot 2^i} = \prod_{i=0}^{k} x^{b_i \cdot 2^i}$,其中 $b_i = 1$ 时贡献一个因子。 - 迭代过程: - 初始化结果为 1。 - 遍历 $n$ 的二进制位(从最低位到最高位)。 - 如果当前位 $b_i = 1$,则将结果乘以当前底数的($x^{2^i}$)。 - 每一步将底数平方(即 $x \leftarrow x^2$),并将指数右移一位($n \leftarrow n / 2$)。 - 例如,计算 $2^5$($n=5$ 的二进制是 $101$): - 初始化:$ \text{ans} = 1 $, 当前底数 $ \text{base} = 2 $. - 第 1 位(最低位):$b_0 = 1$,所以 $\text{ans} = 1 \times 2 = 2$;然后 $\text{base} = 2^2 = 4$,$n \leftarrow 2$(右移)。 - 第 2 位:$b_1 = 0$,不更新 ans;$\text{base} = 4^2 = 16$,$n \leftarrow 1$。 - 第 3 位:$b_2 = 1$,所以 $\text{ans} = 2 \times 16 = 32$;结束。 - 结果 32,仅需 2 次乘法(vs. 4 次循环)。 - 二进制方法易于迭代实现,且高效利用位运算(如 `b & 1` 检查最低位,`b >>= 1` 右移指数)[^2]。 #### 3. **代码实现示例** 参考引用的代码,这里给出一个简化的 Python 实现(基于二进制角度): ```python def fast_power(base, power): result = 1 while power > 0: if power % 2 == 1: # 或使用位运算: if power & 1 result *= base # 当最低位为1时,累乘当前底数 base *= base # 底数平方 power //= 2 # 或使用位运算: power >>= 1, 指数右移 return result # 示例:计算 2^10 print(fast_power(2, 10)) # 输出 1024 ``` - **说明**: - 时间复杂度 $O(\log n)$:因为指数每次减半(或右移),循环次数为 $\log_2 n$。 - 空间复杂度 $O(1)$:仅需常数空间。 - 优化点:使用位运算(如 `&` 和 `>>`)可进一步提升性能[^3]。 #### 4. **为什么高效?** - 每次迭代指数减半,避免冗余计算。例如,计算 $x^{1000000}$,简单方法需 100 万次乘法,而快速只需约 $\log_2(1000000) \approx 20$ 次乘法[^2]。 快速算法广泛应用于加密算法(如 RSA)、大数模运算和算法竞赛中。如果您有具体场景,可以进一步探讨!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值