poj1845

这道题目设计到的知识点比较多。最开始思路有点偏,采用高精度+同余模TLE+滚动数组,后来才知道可以用到性质解题。下面先给出我采用高精度+同余模TLE+滚动数组的代码(TLE且不够完善,仅供扩展思路)

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#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<Max;i++)
		result[i]=0;
	for(i=0;i<len;i++)
		for(j=0;j<len;j++)
			result[i+j]+=a[i]*b[j];
	for(i=0;i<=2*len;i++)
		if(result[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<firstlen;i++)
			for(j=0;j<dymlen;j++){
				Cal_product(dym[index%2][pivot],len[index%2][pivot],record[i],tlen[i],dym[(index+1)%2][j],len[(index+1)%2][j]);
				bool flag=true;
				for(k=0;k<pivot;k++){
					if(len[index%2][pivot]!=len[index%2][k])
						continue;
				    for(s=0;s<len[index%2][k];s++)
						if(dym[index%2][pivot][s]!=dym[index%2][k][s])
							break;
					if(s==len[index%2][k]){
						flag=false;
						break;
					}
				}
				if(flag)
					pivot++;
			}
		dymlen=pivot;
		index++;
		}
		index--;
	    /*printf("\n");
		for(i=0;i<dymlen;i++){
			for(int j=len[index%2][i]-1;j>=0;j--)
				printf("%d ",dym[index%2][i][j]);
			printf("\n");
		}*/
		for(i=0;i<dymlen;i++)
		   Cal_total(dym[index%2][i],len[index%2][i]);
		printf("%d\n",Cal_mod());
}
int main(){
	while(scanf("%d%d",&A,&B)){
    if(A==0 && B!=0)
		printf("0\n");
	else if(B==0 && A!=0)
		printf("1\n");
	else{
		memset(Sum,0,sizeof(Sum));
		finallen=0;
		Cal_result();
	}
	}
	/*a[0]=2;
	b[0]=6;
	b[1]=1;
	int i;
	Cal_product(test,i,a,1,b,2);*/
	return 0;
}

涉及到的知识点有同余模公式+整数唯一分解定理+约数和公式。

一、整数唯一分解定理:

正整数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<stdio.h>
#include<stdlib.h>
#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<k;i++){
			//printf("%d %d\n",prime[i],num[i]);
			  ans=(ans*(Sum(prime[i],num[i]*B)))%mod;		}
		printf("%d\n",ans);
	}
}
		



 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值