USTCOJ 1106 The Luckiest number,2008 Asia Regional, Hefei, Preliminary

这是很明显的数论题,求10模某数的阶.(要构造的数是8*(10 ^ n - 1) / 9, 所以是求10)

题目链接:http://acm.ustc.edu.cn/ustcoj/problem.php?id=1106

注意到一些特殊小情况,其余直接求阶,一是10有因子5,所以,设幸运数是l, l能被5整除则不能被构造,另外,要构造的数中2的次数最多3次,所以模16等于0一概不能被构造,

最后要注意表达式除以9,所以我们最好回避l有因子三的情况,一旦有因子3,除尽为止,保存到另外一个数上, 因为两个互质的数相乘,然后求10关于他们积的阶,等于10关于各自阶的最小公倍数,所以这样把因子三分开处理即可.

由欧拉定理, 如果(10, l) = 1, 10 ^ Eulor(l) mod(l) = 1;

所以需要Eulor函数,但Eulor(l)不一定是阶,所以需要求Eulor(l)的因子,需要求一个数所有因子的函数,

另外Eulor(l)可以很大,求10的Eulor(l)次幂时,需要快速幂算法.

于是写出如下代码;

#include<cstdio>
#include<iostream>
using namespace std;

int gcd(int p, int q)
{
		int t;

		while (q)
		{
				t = q;
				q = p % q;
				p = t;
		}

		return p;
}

int qpow(int m, int n, int mod)
{
		long long re = 1, tem = m;

		while (n)
		{
				if (n & 1)
				{
						re *= tem;
						re %= mod;
				}			
				tem *= tem;
				tem %= mod;
				n >>= 1;
		}

		return re;
}

int euler(int n)
{ 
		 int res = n, a = n;
		for(int i = 2; i * i <= a; i++)
		{
				if(a % i == 0)
				{
						res = res / i * (i - 1);
						while(a % i == 0)
								a /= i;
				}
		}
		if(a > 1)
				res = res / a * (a - 1);

		return res;
}

void factor(int n, int *f, int *l)
{
		int i, j;

		for (i = 1, j = 0; i * i <= n; ++i)
		{
				if (n % i == 0)
				{
						f[j++] = i;
						f[j++] = n / i;
				}
		}
		*l = j;
}

int re(int luck)
{
		int i, len, f[1000] = {0}, l, min = -1;

		while (luck % 2 == 0)
				luck >>= 1;
		l = euler(luck);
		if (l == 1)
				return 1;
		factor(l, f, &len);
		for (i = 1; i < len; ++i)
		{
				if (qpow(10, f[i], luck) == 1)
						min = min < f[i] && min > 0 ? min : f[i];
		}

		return min;
}

int main()
{
		int n = 1, luck, f3, tem1, tem2, res = 0;

		while (scanf("%d", &luck), luck)
		{
				f3 = 9;
				if (luck % 5 == 0 || luck % 16 == 0)
						printf("Case %d: 0\n", n++);
				else
				{
						if(luck % 3 == 0)
						{
								while (luck % 3 == 0)
								{
										f3 *= 3;
										luck /= 3;
								}
								tem1 = re(luck);
								tem2 = re(f3);
								res = tem1 * tem2 / gcd(tem1, tem2);
								printf("Case %d: %d\n", n++, res);
						}
						else
						{
								res = re(luck);
								printf("Case %d: %d\n", n++, res);
						}
				}
		}

		return 0;
}

一些说明:

gcd是为了求最小公倍数;因为lcm (x,y)= x*y / gcd(x,y);

快速幂只需要知道模的结果,所以加了参数mod;

过程中为了防止数据溢出,用long long(不用long long会溢出, 比如1111111113)

factor函数把因子保存在f[1000]中,1000是经过尝试的值一开始估计50000,但事实上2000000000以下的数因子个数最多不超过1000,并把数组中存放了的因子个数信息也返回了.

逐个尝试因子是否模余一, 幷取最小的因子即可.

最后:

这是一道比较令我满意的题目,代码效率较高,比之前AC的最快代码快了一倍吧.

很喜欢数论.^_^

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值