POJ1845 乘法逆元+long long快速幂+质数分解

POJ1845 乘法逆元+快速幂+质数分解

题面
POJ1845题面

思路
显然要对A分解质因数,可得 A B A^B AB的所有约数之和(求模)为:
S u m = ( 1 + p 1 + p 1 2 + … + p 1 B ∗ c 1 ) ∗ ( 1 + p 2 + p 2 2 + … + p 2 B ∗ c 2 ) ∗ ⋯ ∗ ( 1 + p n + p n 2 + … + p n B ∗ c n )   m o d   9901 (1) Sum = (1+p_1+p_1^2+\ldots+p_1^{B*c_1})*(1+p_2+p_2^2+\ldots+p_2^{B*c_2})*\cdots* \\ (1+p_n+p_n^2+\ldots+p_n^{B*c_n}) \space mod\space9901 \tag{1} Sum=(1+p1+p12++p1Bc1)(1+p2+p22++p2Bc2)(1+pn+pn2++pnBcn) mod 9901(1)
那么显然要算积中的每一项(求模),由等比求和公式可得
( 1 + p i + p i 2 + … + p i B ∗ c i )   m o d   9901 = p i B ∗ c i + 1 − 1 p i − 1   m o d   9901 (2) (1+p_i+p_i^2+\ldots+p_i^{B*c_i})\space mod\space9901 = \frac{p_i^{B*c_i+1}-1}{p_i-1} \space mod\space9901 \tag{2} (1+pi+pi2++piBci) mod 9901=pi1piBci+11 mod 9901(2)
显然9901是个质数,只要 ( p i − 1 ) (p_i-1) (pi1)不是9901的倍数,两者就互质,可以用乘法逆元计算,先用快速幂计算分子 ( p B ∗ c 1 + 1 − 1 )   m o d   9901 (p^{B*c_1+1}-1)\space mod\space 9901 (pBc1+11) mod 9901,然后根据费马小定理的推论,乘法逆元即为 ( p i − 1 ) 9899   m o d   9901 (p_i-1)^{9899}\space mod\space 9901 (pi1)9899 mod 9901,也用快速幂计算即可
那么如果 ( p i − 1 ) (p_i-1) (pi1)是9901的倍数怎么办,显然有 p i ≡ 1 ( m o d   9901 ) (3) p_i\equiv1(mod\space 9901) \tag{3} pi1(mod 9901)(3)那么就有
( 1 + p i + p i 2 + … + p i B ∗ c i ) ≡ ( 1 + 1 2 + 1 3 + … + 1 B ∗ c i ) ≡ B ∗ c i + 1 ( m o d   9901 ) (4) (1+p_i+p_i^2+\ldots+p_i^{B*c_i})\equiv(1+1^2+1^3+\ldots+1^{B*c_i})\equiv B*c_i+1(mod\space 9901) \tag{4} (1+pi+pi2++piBci)(1+12+13++1Bci)Bci+1(mod 9901)(4)
所以两种情况分开算就行了

注意事项
1)以下代码中没有一处的long long是没用的,做大数题(基本1e5就要开始注意乘法,时刻注意乘方)一定要时刻注意int的溢出问题(上限也就2e9多一点),每一个数学运算符都要对两侧数字的范围进行判断,然后判断结果会不会溢出(这里mod还比较小,可以用int,再大一点的mod就最好全部用long long了)
注意运算符优先级,先说一下我的理解(这里我也不确定是不是这样的):比如a = (long long)a*b%c,由于()的运算符最高,先将a转化为long long,然后乘以b,最后再模上c(*和%的运算优先级相同,右结合,所以直接从左向右算就行了),最后因为a是int,赋值时又强制类型转换为int,再赋值给a
下面对所有的long long处进行解释:

ans = (ll)ans*a % mod;\\(1)

(1)对于强制类型转换的两个变量:
a n s ∈ [ 0 , 9901 ] ,   a ∈ [ 0 , 5 ∗ 1 0 7 ] (1) ans \in[0,9901],\space a\in[0,5*10^7] \tag{1} ans[0,9901], a[0,5107](1)
注意以上的a只有第一次是这个范围,之后会在[1,9901]之间,但是因为第一次,还得用long long,下面遇到相同的情况同理

a = (ll)a*a % mod;\\(2)
int qpow(int a,ll b)\\(3)

(3)注意主函数调用qpow的时候,如果是分母(乘法逆元算)就没有问题(9989次),但是如果是分子,都是b*c[i]作指数,为了达到c[i]的最大情况,这里我们不妨假设只有2作因子,那么指数最大可以达到
⌊ l o g 2 ( 5 ∗ 1 0 7 ) ⌋ = 25 (3) \lfloor log_2(5*10^{7})\rfloor = 25 \tag{3} log2(5107)=25(3)
那么就有
b ∈ [ 0 , 5 ∗ 1 0 7 ]   c [ i ] ∈ [ 0 , 25 ]   b ∗ c [ i ] ∈ [ 0 , 1.25 ∗ 1 0 9 ] (3) b\in[0,5*10^7] \space c[i]\in[0,25]\space b*c[i]\in[0,1.25*10^9] \tag{3} b[0,5107] c[i][0,25] bc[i][0,1.25109](3)
这里做题时因为没有办法做太精确的运算,当时估计是c[i]大概是个两位数,这就很有可能溢出2e9的多一点的int上限,而且如读者所见,只要这个两位数再多大概十几,就溢出了,因此用long long 是合理的
下面同理,不多说了
(4)

ans= ((ll)b*c[i]+1) % mod *ans % mod;\\(4)

2)对于取余数类的问题,最好不要使用*= +=这类符号,有可能会因为没给赋值的变量取模而出问题(不要省写)
3)对一个数分解质因数时(经验),最多开20个数就够了,前10个质数的乘积就已经基本大于int上限了,如果有更大的质数,肯定有这前十个质数没有的情况,而且大质数在范围内肯定质数更少

反思
1)质因数分解,快速幂不熟
2)对取余类问题不要省写
3)遇到int上限问题,每打一个算式都要想清楚有没有可能溢出,或者有可能越界直接用long long(不就两倍空间嘛)
4)乘法逆元还要再熟悉

代码

#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
int num =0;
const int mod = 9901;
int p[20],c[20];
void divide(int n)
{
    memset(c,0,sizeof(c));
    for(int i = 2; i*i<=n;i++)
    {
        if(n%i == 0) 
        {
            p[++num] = i;//add first cause loop next
            while(n % i == 0) n/=i,c[num]++;
        }
    }
    if(n > 1) p[++num] = n,c[num] = 1;//if n is prime,no segment divide it
}
//Caution
int qpow(int a,ll b)
{
    int ans = 1;
    for(;b;b>>=1)
    {
        if(b & 1)
        ans = (ll)ans*a % mod;
        a = (ll)a*a % mod;
    }
    return ans;
}

int main()
{
    int a,b;
    cin >> a >> b;
    divide(a);
    int ans = 1;
    for(int i = 1;i<=num;i++)
    {
        if((p[i]-1)%mod ==0)
        {
            ans= ((ll)b*c[i]+1) % mod *ans % mod;
        }
        else
        {
            int tmp = (qpow(p[i],(ll)b*c[i]+1)-1+mod)%mod;
            int tmp2 = qpow(p[i]-1,mod-2);
            ans = ans*tmp% mod*tmp2%mod;
        }
    }
    cout << ans <<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在信号处理领域,DOA(Direction of Arrival)估计是一项关键技术,主要用于确定多个信号源到达接收阵列的方向。本文将详细探讨三种ESPRIT(Estimation of Signal Parameters via Rotational Invariance Techniques)算法在DOA估计中的实现,以及它们在MATLAB环境中的具体应用。 ESPRIT算法是由Paul Kailath等人于1986年提出的,其核心思想是利用阵列数据的旋转不变性来估计信号源的角度。这种算法相比传统的 MUSIC(Multiple Signal Classification)算法具有较低的计算复杂度,且无需进行特征值分解,因此在实际应用中颇具优势。 1. 普通ESPRIT算法 普通ESPRIT算法分为两个主要步骤:构造等效旋转不变系统和估计角度。通过空间平移(如延时)构建两个子阵列,使得它们之间的关系具有旋转不变性。然后,通过对子阵列数据进行最小二乘拟合,可以得到信号源的角频率估计,进一步转换为DOA估计。 2. 常规ESPRIT算法实现 在描述中提到的`common_esprit_method1.m`和`common_esprit_method2.m`是两种不同的普通ESPRIT算法实现。它们可能在实现细节上略有差异,比如选择子阵列的方式、参数估计的策略等。MATLAB代码通常会包含预处理步骤(如数据归一化)、子阵列构造、旋转不变性矩阵的建立、最小二乘估计等部分。通过运行这两个文件,可以比较它们在估计精度和计算效率上的异同。 3. TLS_ESPRIT算法 TLS(Total Least Squares)ESPRIT是对普通ESPRIT的优化,它考虑了数据噪声的影响,提高了估计的稳健性。在TLS_ESPRIT算法中,不假设数据噪声是高斯白噪声,而是采用总最小二乘准则来拟合数据。这使得算法在噪声环境下表现更优。`TLS_esprit.m`文件应该包含了TLS_ESPRIT算法的完整实现,包括TLS估计的步骤和旋转不变性矩阵的改进处理。 在实际应用中,选择合适的ESPRIT变体取决于系统条件,例如噪声水平、信号质量以及计算资源。通过MATLAB实现,研究者和工程师可以方便地比较不同算法的效果,并根据需要进行调整和优化。同时,这些代码也为教学和学习DOA估计提供了一个直观的平台,有助于深入理解ESPRIT算法的工作原理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值