【ybt金牌导航8-4-8】最小数(数学)(阶)(欧拉函数)

最小数

题目链接:ybt金牌导航8-4-8

题目大意

给你一个整数 n ,要你找到只由 8 组成的被 n 整除的最小数的位数。

思路

考虑把这些数给表示出来。
(这里就给我整不会了)

\(\dfrac{8(10^x-1)}{9}\) 就是 \(x\) 位的那个数。

然后就可以有式子搞了:
\(\dfrac{8(10^x-1)}{9}\% n=0\)
然后左右和模数都乘 \(9\)
\(8(10^x-1)\% 9n=0\)

显然就是要 \(10^x-1\%9n=0\),即 \(10^x\equiv 1\pmod{9n}\)
(这里的 \(n\) 要除去它跟 \(8\)\(\gcd\)

发现这就是要找啊。
然后阶有一些性质:
有解的情况是下面的底数跟模数的 \(\gcd\)\(1\),即 \(\gcd(10,9n)=1\)

然后阶是 \(\varphi(9n)\) 的,所以我们就直接在它的因子里面找。
(找的方法这里是每个质数分开考虑能不能删掉尽可能多个,因为阶的倍数肯定也满足条件)

然后这里有一个小小的优化(虽然没什么用),就是检查的时候你可以把指数 \(\% \varphi(9n)\)。(这个 \(n\) 是之前的)
因为欧拉定理,既然 \(10\)\(9n\) 互质,就有 \(10^{\varphi(9n)}\equiv1\pmod{9n}\)

然后搞就可以了。

代码

#include<cstdio>
#define ll long long

using namespace std;

ll n, tot, m, r, q, ans, num;

ll mul(ll x, ll y, ll mod) {
	ll z = (long double)x * y / mod;
	ll re = x * y - z * mod;
	if (re < 0) re += mod;
	if (re >= mod) re -= mod;
	return re;
}

ll ksm(ll x, ll y, ll mo) {
	ll re = 1;
	while (y) {
		if (y & 1) re = mul(re, x, mo);
		x = mul(x, x, mo);
		y >>= 1;
	}
	return re;
}

ll phi(ll now) {
	ll re = now;
	for (ll i = 2; i * i <= now; i++) {
		if (now % i == 0) {
			re = re / i * (i - 1);
			while (now % i == 0) now /= i; 
		}
	}
	if (now > 1) re = re / now * (now - 1);
	return re;
}

ll gcd(ll x, ll y) {
	if (!y) return x;
	return gcd(y, x % y);
}

ll check(ll y, ll mo) {
	return (ksm(10, y, mo) - 1 + mo) % mo * 8 % mo;
}

int main() {
	scanf("%lld", &n);
	while (n) {
		printf("Case %lld: ", ++tot);
		
		m = n * 9 / gcd(n, 8);
		r = phi(n * 9);
		
		if (gcd(10, m) != 1) printf("0\n");//判无解
			else {
				q = phi(m); ans = q;//阶肯定在 phi(m) 的约数中
				for (ll i = 2; i * i <= q; i++) {//每个质数分开考虑,每次能弃就弃
					num = 0;
					while (q % i == 0) q /= i, num++;
					while (num && !check((ans / i) % r, n * 9))
						num--, ans /= i;
				}
				if (q > 1 && !check((ans / q) % r, n * 9)) ans /= q;
				printf("%lld\n", ans);
			}
		
		scanf("%lld", &n);
	}
	
	return 0;
}
posted @ 2021-12-11 10:30  あおいSakura  阅读(55)  评论(0编辑  收藏  举报