数论综合题--POJ----1845

原题:


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).

题意:

该题目的题意为求a的b次方,再求其值的所有因数之和并对9901取模。

a,b的范围均为5*10的八次方!!!数比较大!!

思路:

解题思路:(转自网上)

要求有较强数学思维的题

应用定理主要有三个:

要求有较强数学思维的题

应用定理主要有三个:

(1)   整数的唯一分解定理:

任意正整数都有且只有一种方式写出其素因子的乘积表达式。

A=(p1^k1)*(p2^k2)*(p3^k3)*....*(pn^kn)   其中pi均为素数

(2)   约数和公式:

对于已经分解的整数A=(p1^k1)*(p2^k2)*(p3^k3)*....*(pn^kn)

有A的所有因子之和为

S = (1+p1+p1^2+p1^3+...p1^k1) * (1+p2+p2^2+p2^3+….p2^k2) * (1+p3+ p3^3+…+ p3^k3) * .... * (1+pn+pn^2+pn^3+...pn^kn)

(3)   同余模公式:

(a+b)%m=(a%m+b%m)%m

(a*b)%m=(a%m*b%m)%m

有了上面的数学基础,那么本题解法就很简单了:

1: 对A进行素因子分解

分解A的方法:

A首先对第一个素数2不断取模,A%2==0时,记录2出现的次数+1,A/=2;

当A%2!=0时,则A对下一个连续素数3不断取模...

以此类推,直到A==1为止。

注意特殊判定,当A本身就是素数时,无法分解,它自己就是其本身的素数分解式。

最后得到A = p1^k1 * p2^k2 * p3^k3 *...* pn^kn.
      故 A^B = p1^(k1*B) * p2^(k2*B) *...* pn^(kn*B);


2:A^B的所有约数之和为:

     sum = [1+p1+p1^2+...+p1^(a1*B)] * [1+p2+p2^2+...+p2^(a2*B)] *...* [1+pn+pn^2+...+pn^(an*B)].


3: 用递归二分求等比数列1+pi+pi^2+pi^3+...+pi^n:

(1)若n为奇数,一共有偶数项,则:
      1 + p + p^2 + p^3 +...+ p^n

      = (1+p^(n/2+1)) + p * (1+p^(n/2+1)) +...+ p^(n/2) * (1+p^(n/2+1))
      = (1 + p + p^2 +...+ p^(n/2)) * (1 + p^(n/2+1))

上式红色加粗的前半部分恰好就是原式的一半,那么只需要不断递归二分求和就可以了,后半部分为幂次式,将在下面第4点讲述计算方法。

(2)若n为偶数,一共有奇数项,则:
      1 + p + p^2 + p^3 +...+ p^n

      = (1+p^(n/2+1)) + p * (1+p^(n/2+1)) +...+ p^(n/2-1) * (1+p^(n/2+1)) + p^(n/2)
      = (1 + p + p^2 +...+ p^(n/2-1)) * (1+p^(n/2+1)) + p^(n/2);

上式红色加粗的前半部分恰好就是原式的一半,依然递归求解

4:反复平方法计算幂次式p^n

这是本题关键所在,求n次幂方法的好坏,决定了本题是否TLE。

以p=2,n=8为例

常规是通过连乘法求幂,即2^8=2*2*2*2*2*2*2*2

这样做的要做8次乘法

而反复平方法则不同,

定义幂sq=1,再检查n是否大于0,

While,循环过程若发现n为奇数,则把此时的p值乘到sq

{

n=8>0 ,把p自乘一次, p=p*p=4     ,n取半 n=4

n=4>0 ,再把p自乘一次, p=p*p=16   ,n取半 n=2

n=2>0 ,再把p自乘一次, p=p*p=256  ,n取半 n=1,sq=sq*p

n=1>0 ,再把p自乘一次, p=p*p=256^2  ,n取半 n=0,弹出循环

}

则sq=256就是所求,显然反复平方法只做了3次乘法

WA点:

1.分解素因子:
不要加素数判断,否则会TLE。

void  prime(__int64 n){
	__int64 num=0,x=n;
		for(__int64 i=2;i*i<x;){
			if(x%i==0){
				num=0;
				while(x%i==0){
					x=x/i;
					num++;
				}
				p[sum]=i;k[sum]=num;
				sum++;
			}
			if(i==2) i++;
			else i+=2;
		}
		if(x!=1){
			p[sum]=x;
			k[sum++]=1;
		}
}

 2.分治法求等比数列:
 

ans=(ans*(slove(p[i],k[i]*b)%mod))%mod;
__int64 slove(__int64 p,__int64 c){
	if(c==0) return 1;
	if(c%2==1) return (slove(p,c/2)*(1+fpow(p,c/2+1)))%mod;
	if(c%2==0) return (slove(p,c/2-1)*(1+fpow(p,c/2+1))+fpow(p,c/2))%mod;
}

3.一个很奇怪的点

poj上用__int128会PE,只能用__int64,注意一定要用__int64,不然会溢出,long long会溢出,直接WA。

AC代码:

#include<cstdio>  
#include<cstring>  
#include<cstdlib>  
#include<cctype>  
#include<cmath>  
#include<iostream>  
#include<sstream>  
#include<iterator>  
#include<algorithm>  
#include<string>  
#include<vector>  
#include<set>  
#include<map>  
#include<stack>  
#include<deque>  
#include<queue>  
#include<list> 
using namespace std;
typedef long long ll;
//快读和快写
inline __int64 read()
{
	__int64 s=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')
			f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*f;
}

inline void write(__int64 x)
{
    if(x<0){
    	putchar('-');
		x=-x;
	}
    if(x>9) 
		write(x/10);
    putchar(x%10+'0');
}

__int64 sum=0;
const __int64  maxn=1e6+10;
const __int64 mod=9901;
__int64  p[maxn];
__int64  k[maxn];
void  prime(__int64 n){
	__int64 num=0,x=n;
		for(__int64 i=2;i*i<x;){
			if(x%i==0){
				num=0;
				while(x%i==0){
					x=x/i;
					num++;
				}
				p[sum]=i;k[sum]=num;
				sum++;
			}
			if(i==2) i++;
			else i+=2;
		}
		if(x!=1){
			p[sum]=x;
			k[sum++]=1;
		}
}


__int64 fp(__int64 a,__int64 b,__int64 p){
	__int64 ans=1%p;
	a%=p;
	while(b){
		if(b&1) ans=(ans*a)%p;
		a=(a*a)%p;
		b=b>>1;
	}
	return ans;
}

__int64 fpow(__int64 a,__int64 b){
	__int64 ret=1;
	while(b){
		if(b&1) ret=(ret*a)%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ret;
}

__int64 slove(__int64 p,__int64 c){
	if(c==0) return 1;
	if(c%2==1) return (slove(p,c/2)*(1+fpow(p,c/2+1)))%mod;
	if(c%2==0) return (slove(p,c/2-1)*(1+fpow(p,c/2+1))+fpow(p,c/2))%mod;
}

int  main(){
	std::ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    ll a,b;
    a=read();
    b=read();
    prime(a);

	__int64 ans=1;

	for(__int64 i=0;i<sum;i++){
		ans=(ans*(slove(p[i],k[i]*b)%mod))%mod;
		ans=ans%mod;
	}
    write(ans);
    printf("\n");
	return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值