这道题目设计到的知识点比较多。最开始思路有点偏,采用高精度+同余模TLE+滚动数组,后来才知道可以用到性质解题。下面先给出我采用高精度+同余模TLE+滚动数组的代码(TLE且不够完善,仅供扩展思路)
#include
#include
#include
#include
#define Max 3500
#define Maxx 3000
#define Prime 9901
int record[Maxx][Max];
int tlen[Maxx];
int Sum[Max];
int dym[2][Maxx][Max];
int len[2][Maxx];
int A,B,dymlen,finallen,firstlen;
void Cal_product(int *result,int &rlen,int *a,int alen,int *b,int blen){
int len=(alen>blen)?alen:blen;
int i,j;
for(i=len;i>=alen;i--) a[i]=0;
for(i=len;i>=blen;i--) b[i]=0;
for(int i=0;i=10){
result[i+1]+=result[i]/10;
result[i]%=10;
}
for(i=2*len;i>=0;i--)
if(result[i]>0)
break;
rlen=i+1;
}
void Cal_total(int *a,int alen){
int i,len=(alen>finallen)?alen:finallen;
for(i=len;i>=alen;i--) a[i]=0;
for(i=len;i>=finallen;i--) Sum[i]=0;
for(i=0;i<=len;i++){
Sum[i]+=a[i];
if(Sum[i]>=10){
Sum[i+1]+=Sum[i]/10;
Sum[i]%=10;
}
}
for(i=len;i>=0;i--)
if(Sum[i]>0)
break;
finallen=i+1;
}
int Cal_mod(){
int point=0;
for(int i=finallen-1;i>=0;i--)
point=(point*10+Sum[i])%Prime;
return point;
}
void Cal_result(){
int i,j,k,s,pivot1=0,pivot2;
for(i=1;i<=A;i++){
if(A%i==0){
pivot2=0;
int temp=i;
while(temp>0){
dym[0][pivot1][pivot2]=record[pivot1][pivot2]=temp%10;
pivot2++;
temp/=10;
}
len[0][pivot1]=pivot2;
tlen[pivot1]=pivot2;
pivot1++;
}
}
firstlen=dymlen=pivot1;
int index=1,bb=B-1;
while(bb--){
int pivot=0;
for(i=0;i=0;j--)
printf("%d ",dym[index%2][i][j]);
printf("\n");
}*/
for(i=0;i
涉及到的知识点有同余模公式+整数唯一分解定理+约数和公式。
一、整数唯一分解定理:
正整数N可以唯一分解为如下的形式:
N=(p1^k1)*(p2^k2)*(p3^k3)*....*(pn^kn) 其中pi均为素数,ki大于0
二、约数和公式
对于正整数N=(p1^k1)*(p2^k2)*(p3^k3)*....*(pn^kn) ,其所有因子(包含1和其本身)的和公式如下:
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)
三、同余模定理:
(a+b)%mod=(a%mod+b%mod)%mod;
(a*b)%mod=((a%mod)*(b%mod))%mod;
(a-b)%mod=(a%mod-b%mod)%mod;
知道了上面的知识点后,这倒题目思路就很清晰了:
1、首先对分解A为素数相乘形式。思路为:首先判断A%2是否为0,若为0,则用2不断除以A,直到A不能被2整除为止。然后判断下一个素数3,同样的处理,依次判断5、7、……1直至素数的平方大于A。
2、计算A所有因子的和:
已经计算出A=(p1^k1)*(p2^k2)*(p3^k3)*....*(pn^kn) ;那么和A^B=(p1^k1*B)*(p2^k2*B)*(p3^k3*B)*....*(pn^kn*B) ;那么A^B所有因子的和计算公式如下:
S = (1+p1+p1^2+p1^3+...p1^k1*B) * (1+p2+p2^2+p2^3+….p2^k2*B) * (1+p3+ p3^3+…+ p3^k3*B) * .... * (1+pn+pn^2+pn^3+...pn^kn*B)
3、递归二分求等比数列(1+p+p^2+p^3+...p^k) k>=0
设N=(1+p+p^2+p^3+...p^k)
那么当k为偶数时。共有奇数项,计算公式如下:
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);
上面左边的黑色部分为原式一半,可递归求解。即令SUM(p,n)=1 + p + p^2 + p^3 +...+ p^n。那么当n为偶数时,SUM(p,n)=SUM(p,n/2-1)*(1+p^(n/2+1))+p^(n/2);
当k为奇数时,共有偶数项,计算公式如下:
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))
上面左边黑色部分为原式一半,亦可采用递归二分求解。那么当n为奇数时,SUM(p,n)=SUM(p,n/2)*(1+p^(n/2+1));而递归结束条件应该为n为0时,此时SUM应该返回1
4、反复平方法计算幂次式p^n (p与n不可同时为0)
一般的方法计算p^n幂需循环n次,例如2^8=2*2*2*2*2*2*2*2;
而反复平方法计算p^n幂。则只需要执行3次循环。效率大大提高了。具体看程序。
下面是代码: 144K+0MS
#include
#include
#define Max 10000
#define mod 9901 //取模
int prime[Max]; //保存素数
int num[Max]; //保存相应素数幂
int A,B;
__int64 Power(__int64 p,__int64 n){ // 反复平方法计算幂次式(p^n)%mod
__int64 sq=1;
while(n>0){
if(n&1) // 若为奇数
sq=(sq*p)%mod; //乘以p
n>>=1;
p=(p*p)%mod;
}
return sq;
}
__int64 Sum(__int64 p,__int64 n){ // 二分递归法求解等比数列(1 + p + p^2 + p^3 +...+ p^n)%mod
if(n==0)
return 1;
if(n&1) //奇数
return (Sum(p,n/2)*(1+Power(p,n/2+1))%mod)%mod; //同余模公式+递推公式
else //偶数
return ((Sum(p,n/2-1)*((1+Power(p,n/2+1))%mod))%mod+Power(p,n/2))%mod;
}
int main(){
scanf("%d%d",&A,&B);
if(A==0 && B!=0) // 若0^B,则为0
printf("0\n");
else if(A!=0 && B==0) //若A^0,则为1
printf("1\n");
else{
int i=2,k=0;
while(i*i<=A){ // 筛法求分解式
if(A%i==0){ //若整除
prime[k]=i; //保存该数
int j=0; //初始化个数为0
while(A%i==0){
A/=i;
j++; // 个数增加
}
num[k++]=j; //赋值素数幂次数
}
if(i==2) //若为2,则下一个素数为3
i++;
else //若为奇数则直接将偶数筛掉,枚举奇数
i+=2;
}
if(A!=1){ //若A为素数则增加该素数和其幂
prime[k]=A;
num[k++]=1;
}
int ans=1; //同余模公式(a*b)%mod=((a%mod)*(b%mod))%mod;
for(i=0;i