您的位置:首页 > 其它

POJ 1845 Sumdiv 逆元 费马小定理 Trick

2015-10-03 13:01 344 查看

原题见POJ 1845

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

首先把A分解成素数的幂的形式AB=ps1B1ps2B2...pskBk再求和sum=ps1B1−1p1−1ps2B2−1p2−1...pskBk−1pk−1

因为被模除的9901是个质数,所以对于上面的pn−1部分,可以利用费马小定理,得到pn−1≡pn%(9901−1)−1(mod9901)

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

但是Trick来了!当p-1是9901的倍数时,不存在逆元!

此时再审视一番原式,其实这是等比数列化过来的式子,原式是这样的pn−1p−1=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
, prim
, fq
, 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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: