搜索
您的当前位置:首页正文

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

来源:步旅网

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;
}

因篇幅问题不能全部显示,请点此查看更多更全内容

Top