POJ 1845 Sumdiv (因子和)

28 篇文章 0 订阅
Sumdiv
Time Limit: 1000MS Memory Limit: 30000K
Total Submissions: 15404 Accepted: 3800

Description

Consider two natural numbers A and B. Let S be the sum of all natural divisors of A^B. Determine S modulo 9901 (the rest of the division of S by 9901).

Input

The only line contains the two natural numbers A and B, (0 <= A,B <= 50000000)separated by blanks.

Output

The only line of the output will contain S modulo 9901.

Sample Input

2 3

Sample Output

15
这个题和HDU的1452基本上一样,只不过这里是HDU的推广。

先说这个题的解题步骤:
① 将A分解A=(p1^a1)*(p2^a2)*.....*(pk^ak),那么A^B就是A^B=[p1^(a1*B)] * [p2^(a2*B)] *.....*[pk^(ak*B)]
② 根据约数和的公式求和S并mod9901
别急,下面还有一些细节问题:

这个题我一开始就是先分解,然后按约数和的公式算.但是一直WA,我改了许多地方都不行。
如果一个数n=(p1^a1)*(p2^a2)*.....*(pk^ak)
约数和s=(p1^(a1+1)-1)/(p1-1)   *  (p2^(a2+1)-1)/(p2-1)  *.....* (pk^(ak+1)-1)/(pk-1) 
我就是按这个求s的公式算的,但是一直不对,肯定是因为这个公式中涉及到了除法,虽然在模运算中出现除法可以借助逆元来解决,但是因为求逆元是有条件限制的(a在模m意义下存在逆元的充要条件是a和m互素),我想是肯定是在处理这个地方的时候一直没处理好,所以一直WA。

其实是我忽略了另一个公式,我上面给的求s公式是变形之后的,变形之前是:
s=(1+p1+p1^2+p1^3+...p1^a1) * (1+p2+p2^2+p2^3+….p2^a2) * (1+p3+ p3^3+…+ p3^a3) * .... * (1+pk+pk^2+pk^3+...pk^ak)
这个公式是不涉及除法的,用起来会很舒服。(而对这个公式每个括号里都是一个等比数列,分别对其求和就是上面的那个带除法的公式)。
至此问题就明了了。

那么就抛开这个题看下面的知识:
类似于这样的一个问题:S(k)=A^0+A^1+A^2+....+A^k的快速求和。
方法:二分+递归求解。
下面看个简单例子:
① k=4为偶数
A^0 + A^1 + A^2 + A^3 + A^4
=(A^0 + A^1)+A^2 + A^3(A^0 + A^1)=(1+A^3)*[A^0 + A^1]+A^2=(1+A^(k/2+1))*S(k/2-1)+A^(n/2) 
② k=5为奇数
A^0 + A^1 + A^2 + A^3 + A^4 + A^5
=[A^0 + A^1 + A^2] + A^3*[A^0 + A^1 + A^2]  =(1+A^3)*[A^0 + A^1 + A^2] = (1+A^(k/2+1))*S(k/2)
上面式子中,蓝色部分用快速幂,红色部分继续递归,这样就可以快速求出S(k)了。
递归出口n==0 return 1;

代码中divi[i][0]代表第i个素因子,
           divi[i][1]代表这个素数的次数。

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
using namespace std;
typedef __int64 ll;

const int MOD=9901;
ll divi[10000][2],tot;

ll quick_mod(ll a,ll b){		//a^b%MOD
	ll res=1;
	a%=MOD;
	while(b){
		if(b&1) res=(res*a)%MOD;
		b/=2;
		a=(a*a)%MOD;
	}
	return res;
}

ll cal(int p,int n){
	if(n==0) return 1;
	if(n&1)
		return (1+quick_mod(p,n/2+1))*cal(p,n/2)%MOD;
	else
		return ((1+quick_mod(p,n/2+1))*cal(p,n/2-1)+quick_mod(p,n/2))%MOD;
}

void pre_solve(ll n){
	ll i;
	tot=0;
	for(i=2;i*i<=n;){
		if(n%i==0){
			divi[tot][0]=i;
			divi[tot][1]=0;
			do{
				n/=i;divi[tot][1]++;
			}while(n%i==0);
			tot++;
		}
		if(i==2) i++;
		else i+=2;
	}
	if(n>1){
		divi[tot][0]=n;divi[tot][1]=1;tot++;
	}
}

int main()
{
	ll A,B,i,res;
	while(scanf("%I64d%I64d",&A,&B)!=EOF){
		if(A==0){			//别忘了特判
			printf("0\n");continue;
		}
		if(A==1 || B==0){
			printf("1\n");continue;
		}
		pre_solve(A);
		res=1;
		for(i=0;i<tot;i++)
			divi[i][1]*=B;
		for(i=0;i<tot;i++){
			res=(res*cal(divi[i][0],divi[i][1]))%MOD;
		}
		printf("%I64d\n",res);
	}
	return 0;
}

另给出HDU 1452题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=1452
关于HDU 1452题目的补充知识:http://blog.csdn.net/u013068502/article/details/45251321

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值