POJ 1845 Sumdiv 逆元 费马小定理 Trick

原题见POJ 1845

给A,B,求 AB 的所有因子的和模9901。

首先把A分解成素数的幂的形式

AB=ps1B1ps2B2...pskBk
再求和
sum=ps1B11p11ps2B21p21...pskBk1pk1

因为被模除的9901是个质数,所以对于上面的 pn1 部分,可以利用费马小定理,得到

pn1pn%(99011)1(mod9901)

对于下面的部分,可以直接求逆元,将除的部分转化为成分。

但是Trick来了!当p-1是9901的倍数时,不存在逆元!
此时再审视一番原式,其实这是等比数列化过来的式子,原式是这样的

pn1p1=p0+p1+p2+...+pn
代入 p=9901x+1 ,则原式即为 n+1 特判一下这种情况即可!

附code

/*--------------------------------------------
 * File Name: POJ 2992
 * Author: Danliwoo
 * Mail: Danliwoo@outlook.com
 * Created Time: 2015-10-03 09:53:42
--------------------------------------------*/

#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
using namespace std;
#define N 7100
int num[N], prim[N], fq[N], cnt = 0;

int extend_Euclid(int a, int b, int &x, int &y){
    if(b==0){
    x = 1; y = 0;
    return a;
    }
    int r = extend_Euclid(b, a%b, y, x);
    y -= a/b*x;
    return r;
}
int anti(int a, int m)
{
    int x, y;
    extend_Euclid(a, m, x, y);
    return (x%m+m)%m;
}
void set()
{
    for(int i = 0;i < 7100;i++) num[i] = 1;
    for(int i = 2;i < 7100;i++) if(num[i])
    {
        fq[i-1] = anti(i-1, 9901);
        prim[cnt++] = i;
        for(int j = i*i;j < 7100;j += i)
            num[j] = 0;
    }
}
int po(int a, int k, int m)
{
    if(k == 0) return 1;
    if(k == 1) return a;
    int t = po(a, k/2, m);
    t = t*t%m;
    if(k & 1LL)
        t *= a;
    return t%m;
}
int main()
{
    set();
    int a, b, m = 9901;
    while(~scanf("%d%d", &a, &b))
    {
        if(a == 0)
        {
            printf("0\n");
            continue;
        }
        int ans = 1;
        int t = a;
        for(int i = 0;i < cnt && prim[i]*prim[i] <= a;i++) if(t%prim[i] == 0)
        {
            int s = 0;
            while(t % prim[i] == 0)
            {
                t /= prim[i];
                s++;
            }
            ans = ans*fq[prim[i]-1]%m*(po(prim[i], b%(m-1)*s%(m-1)+1, m)-1)%m;
        }
        if(t != 1)
        {
            if(t % m == 1)
                ans = ans*(b%m+1)%m;
            else
                ans = ans*anti(t-1, m)%m*(po(t%m, b%(m-1)+1, m)-1)%m;
        }
        printf("%d\n", (ans%m+m)%m);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值