【POJ 3696】高次同余方程

1.题目链接。题目大意:给一个L,问存不存在一个数,这个数的数位上全是8并且整除L。如果存在,输出这个数的位数,否则输出0.

2.分析:开始看以为是一个水题,直接暴力枚举ll范围内的所有数据,然后发现不行。仔细分析应该时这样做。

我们知道对于所有的数位上全是A的数其实可以这样写:\frac{A}{9}*(10^{x}-1).那么全是8的数据通项公式就是:\frac{8}{9}*(10^{x}-1).这个数据能够整除L。写成等式就是:\frac{8}{9}*(10^{x}-1)=k*L.下面来推导这个式子。

证明过程:

                                                   \\ \frac{8}{9}*(10^{x}-1)=k*L->8*(10^{x}-1)=9*k*L\\ assume \quad that \quad t \quad is \quad gcd \quad of \quad 8 \quad and \quad 9*L\\ we \quad that \quad 8 \quad 9 \quad is \quad co-prime.\\ so \quad gcd(8,9*L)=gcd(8,L)

然后我们两边同时除以gcd(8,L)并且整理得到:

                                                 10^{x}\equiv1(mod \quad 9*L/gcd(8,L))

然后,整理到这里,我们就需要知道一些很重要的知识了,关于高次同余理论中,有两个很重要的结论:

                                                   a^{x}\equiv1(mod \quad m)

这个方程并不总是有解的,只有在gcd(a,m)=1的时候,才有解。gcd(a,m)不等于1的时候无解。并且在有解的情况下,若存在x0满足上式,那么x0一定整除phi(m).其实这一点很好理解,从欧拉定理就可以看出来。就不再证明了。

知道了这些,这道题也就基本解决了。我们假设:m=9*L/gcd(8,L)的。那么我们只需要判断gcd(10,m)==1?,如果是的,我们就枚举phi(m)的因子,遍历输出最小的即可。当然了,在验证过程我们是需要使用快速幂的,但是因为快速幂在乘的时候会爆ll,所以采用快速乘+快速幂的形式即可。代码如下:

#include<iostream>
#include<cmath>
#include<stdio.h>
#include<cstdio>
#include<algorithm>
#include<vector>
#pragma warning(disable:4996)
using namespace std;
#define ll long long
vector<ll>vec;
ll gcd(ll a, ll b) {

	return b ? gcd(b, a%b) : a;
}
ll phi(ll n)
{
	ll res= n;
	for (int i = 2; i*i<=n; i++)
	{
		if (n % i == 0)
		{
			res = res - res / i;
			while (n%i == 0)n /= i;
		}
	}
	if (n > 1)res = res - res / n;
	return res;
}
ll qmul(ll a, ll b, ll mod)
{
	ll ans = 0;
	while (b)
	{
		if (b & 1)
			ans = (ans + a) % mod;
		a = (a + a) % mod;
		b >>= 1;
	}
	return ans;
}
ll qpow(ll a, ll b, ll mod)
{
	ll res = 1;
	while (b)
	{
		if (b & 1)
			res = qmul(res, a, mod);
		a = qmul(a, a, mod);
		b >>= 1;
	}
	return res;
}

int main()
{
	int cas = 0;
	ll l, m;
	while (scanf("%lld", &l) && l)
	{
		vec.clear();
		if (l == 0)break;
		m = 9 * l / gcd(8, l);
		if (gcd(10, m) != 1)
		{
			printf("Case %d: %d\n", ++cas, 0);
			continue;
		}
		ll ans = phi(m);
		for (ll i = 1; i*i <= ans; i++)
		{
			if (ans%i == 0)
			{
				vec.push_back(i);
				if (ans != i * i)
					vec.push_back(ans / i);
			}
		}
		sort(vec.begin(), vec.end());
		ll i = 0;
		for (i; i <vec.size(); i++)
		{
			if (qpow(10, vec[i], m) == 1)
			{
				break;
			}
		}
		printf("Case %d: %lld\n", ++cas, vec[i]);
	}
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值