[luogu] P4861 按钮
[luogu] P4861 按钮
1.题目
题目背景
\(Ada\)被关在了一个房间里。
题目描述
房间的铁门上有一个按钮,还有一个显示屏显示着“1”。
旁边还有一行小字:“这是一个高精度\(M\)进制计算器,每按一次按钮,屏幕上的数便会乘以\(K\)。当个位数再次变为\(1\)时,门就开了。”
由于\(Ada\)急于出去,所以你要在\(1s\)之内求出她的最小按键次数。
输入输出格式
输入格式:
一行,两个整数\(M\)和\(K\)。
输出格式:
一行一个数字,表示最小按键次数。
如果无论\(Ada\)按多少次都无法让门打开,输出"Let's go Blue Jays!"(不含引号)。输入输出样例
输入样例#1:
11 2
输出样例#1:
10
输入样例#2:
6 26
输出样例#2:
Let's go Blue Jays!
说明
对于\(30\%\)的数据,\(2 \leqslant M,K \leqslant 10^4\)。
对于\(100\%\)的数据,\(2 \leqslant M,K \leqslant 2×10^9\)。
\(Update\):我们不认为个位为11,21,...为问题的解(例如,11在16进制下记为B)
2.题解
数竟出的题真恐怖
30pts
可以开一个\(unordered_map\)判重,然后模拟乘的过程。
不用想,对是对,但是你280ms过是不存在的。
100pts
考虑求\(K^r \equiv 1 (mod~M)\)最小解( \(r\) 在数论中称作阶)。此方程有解当且仅当\(gcd(K, M) = 1\)。
假设所求方程中\(gcd(K, M) = 1\),则由欧拉定理得\(K^{\phi(M)} \equiv 1(mod~M)\)。但\(\phi(M)\)不一定是最小的\(r\)。
阶有一个善良的性质,即\(r | \phi(M)\)。这样我们可以\(O(\sqrt{\phi(M)})\)枚举因子,然后求可以使方程成立的因子的最小值即可。
#include <cstdio>
#include <cmath>
#include <algorithm>
typedef long long ll;
ll k, m, ans(0x7ffffffffffff), a(1);
using std::swap; using std::exit; using std::min;
inline ll gcd(ll a, ll b){
if(a < b) swap(a, b);
return (!(b) ? a : gcd(b, a % b));
}
inline ll euler_phi(ll x) {
ll ans(x), tmp(x);
ll xx = ll(ceill(sqrtl((long double)x)));
for (ll i = 2; i <= xx; ++i)
if(!(tmp % i)) {
ans = ans / i * (i - 1);
while(!(tmp % i)) tmp /= i;
}
if(tmp > 1) ans = ans / tmp * (tmp - 1);
return ans;
}
inline ll f_pow(ll a, ll b) {
ll base(a), ans(1);
while(b) {
if(b & 1) ans = (ans * base) % m;
base = (base * base) % m;
b >>= 1;
}
return ans;
}
int main() {
scanf("%lld%lld", &m, &k);
if(gcd(m, k) != 1) {puts("Let's go Blue Jays!"); exit(0);}
ll phi = euler_phi(m);
ll p = ll(ceill(sqrtl((long double)phi)));
for (ll i = 1; i <= p; ++i)
if(!(phi % i)) {
(f_pow(k, i) == 1) ? ans = min(ans, i) : false;
(f_pow(k, phi / i) == 1) ? ans = min(ans, phi / i) : false;
}
printf("%lld\n", ans);
return 0;
}