POJ 3696 The Luckiest number

题目链接:http://poj.org/problem?id=3696

题意:给一个数N(1<=N<=2000000000);问是否存在N的倍数M,且M的各个位全部由8组成,如果存在多个取最小的M

并输出M由几个8组成。


解题思路:因为M全部由8组成,即M=(10^x -1)*8/9=k*N;

则    (10^x-1)*8/gcd(8,N)=9*k*N/gcd(8,N);

令p=8/gcd(8,N);        q=9*N/gcd(8,N);    即    (10^x-1)*p=k*q;

 由于p和q互质,则(10^x-1)%q==0;

根据同余定理可知,10^x ≡1(mod q)

根据欧拉定理可知当gcd(a,b)==1时,a^φ(b)≡1(mod b);

即可得出:当gcd(10,q)==1时    10^φ(q)≡1(mod q)   即通过枚举φ(q)的因子(最小因子)就能得出结果

由于N比较大,因此采用long long 型。同时做一个素数表。

在进行幂求模运算的时候可以采用快速幂的方法。

由于在进行快速幂求模的时候数据会超出long long 的表示范围,因此要进行优化。

 
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<vector>
using namespace std;
const int M=50000;
int p[M],v[M];   //v[M]用于保存M以内的所有素数
vector<int> T;
long long gcd(long long a,long long b)   //求最大公约数
{
	long long x,y,z;
	if(a>b){x=a;y=b;}
	else {x=b;y=a;}
	while(y)
	{
		z=x%y;x=y;y=z;
	}
	return x;
}
void prime()         //求出M以内的所有素数
{
	memset(p,0,sizeof(p));int k=0;
	for(int i=2;i<M;i++)
	{
		if(!p[i])
		{
			v[k++]=i;
			for(int j=i+i;j<M;j+=i)p[j]=1;
		}
	}
}
long long phi(long long m)          //求m的欧拉值
{
	long long count=m;
	for(int i=0;v[i]*v[i]<=m;i++)
	{
		if(m%v[i]==0)
		{
			count=count/v[i]*(v[i]-1);
			while(m%v[i]==0)m/=v[i];
		}
	}
	if(m>1)count=count/m*(m-1);
	return count;
}
long long fun(long long a,long long b,long long c)   //求a*b(mod c);
{
	a%=c;
	long long s=0;
	while(b)
	{
		if(b&1)
		{
			s=s+a;
			if(s>=c)
				s=s-c;
		}
		a=a<<1;
		if(a>=c)a=a-c;
		b=b>>1;
	}
	return s;
}
long long power_mod(long long a,long long b,long long c)   //快速幂求a的b次方对c取余
{
	long long s=1;a=a%c;
	while(b>=1)
	{
		if(b&1)s=fun(s,a,c);
		a=fun(a,a,c);
		b=b>>1;
	}
	return s;
}
void fs(long long m)
{
	T.clear();
	for(int i=0;v[i]*v[i]<=m;i++)
	{
		while(m%v[i]==0){T.push_back(v[i]);m/=v[i];}
	}
	if(m>1)T.push_back(m);
}
int main()
{
	long long m;int k=0;prime();
	while(scanf("%lld",&m)&&m)
	{
		k++;printf("Case %d: ",k);
		m=9*m/gcd(8,m);
		if(gcd(m,10)!=1)printf("0\n");
		else
		{
			long long n=phi(m);long long mi=n;bool flat=true;
			while(flat)
			{
				fs(n);
				flat=false;
				for(int i=0;i<T.size();i++)
				{
					if(power_mod(10,n/T[i],m)==1)
					{flat=true;if(n/T[i]<mi)mi=(n/T[i]);}
				}
				n=mi;
			}
			printf("%lld\n",mi);
		}
	}
}   

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值