欧拉定理,对于正整数a,n,若gcd(a,n)=1,则有a^euler(n)=1(mod n)。
快速幂取模算法,只要p*2在long long范围内都可以计算,对于算法中long long * long long的情况可能超界,但是我们可以用一种类似于快速幂的方法来进行次乘法计算,即快速幂是用乘法代替幂计算以便及时取模,而此算法则是用加法代替乘法计算及时取模,把快速幂中的乘法换成加法,平方换成乘2即可。
题意:给出数字L,问一个各位数字都是8的L的倍数是否存在,若存在,输出其最小长度。
分析:欧拉定理,我们可以列出方程(10^x - 1) / 9 = L * k / 8,推出(10^x - 1) * 8 = k * 9L,我们现在想要把它化成10^x=1(mod p)的形式,因此我们要看能从这个等式中找到10^x-1是谁的倍数,且要保证p中一定要包含L,且不包含k。我们需要这样做,让等式变为(10^x-1)*a=k*b的形式,且b中要包含L且不包含k,要求gcd(a,b)=1。如果满足以上条件我们就可以证明(10^x-1)是b的倍数了,因为要使等式成立,组成b的因子在等式左边,没有包含在a中,则必然包含在(10^x-1)中。现在若gcd(8,9L)=1则等式符合要求了,但是无法证明它等于1,所以我们将等式改写(10^x-1)*(8/gcd(8,9L))=k*(9L/gcd(8,9L)),这回a,b就互质了。所以我们让p=9L/gcd(8,9L)。
然后我们就有10^x-1=0(mod p),即10^x=1(mod p)。这样就符合了欧拉定理的形式。
现在若gcd(10, p)=1则x必然有解,因为x=phi(p)即可。但我们现在要求的是最小正整数解,mod p乘法是有循环节的,即设循环节为r,那么10^x==10^(x%r)。x=0的时候10^0=1(mod p),有因为在一个循环中乘法结果是不会有重复数字的,即对于xi != xj且xi < r, xj < r,则有10^xi != 10^xj。因此要使得等式10^x=1(mod p)成立,必须有x%r=0。因此最小正整数解就是第一个循环节。那么既然phi(p)是解说明它在循环节上,是循环节的倍数,因此只要枚举phi(p)的所有约数其中一定有循环节,找到最小的可以使得等式成立的即为所求。
对于gcd(10, p) != 1的情况,方程一定无解。设gcd(10,p)=g,因为10^x % g = 0,可以看作10^x=g*y。设p=g*z,10^x %p = (g*y)%(g*z) = g*y-n*(g*z) = g*(y-n*z) 所以一定是g的倍数,不会等于1。
综上,我们代码处理过程为,先求phi(p),p=9L/gcd(8,9L)。然后用跟好phi(p)的效率求其所有约数,对于每个约数,用快速幂验证是否满足方程10^x=1(mod p)。找到满足方程的最小的一个。
#include <iostream> #include <algorithm> #include <cstdlib> #include <cstdio> #include <cstring> using namespace std; #define maxn (1 << 16) long long n; long long factor[maxn]; long long euler(long long n) { long long ret = n; for (long long i = 2; i * i <= n; i++) if (n % i == 0) { ret = ret / i * (i - 1); while (n % i == 0) n /= i; } if (n > 1) ret = ret / n * (n - 1); return ret; } long long mult(long long a, long long b, long long p) { long long ret = 0; while (b) { if (b & 1) ret = (ret + a) % p; a = 2 * a % p; b >>= 1; } return ret; } long long power(long long x, long long n, long long p) { long long ret = 1; x %= p; while (n) { if (n & 1) ret = mult(ret, x, p); x = mult(x, x, p); n >>= 1; } return ret; } long long work() { n *= 9; for (int i = 0; i < 3; i++) if (n % 2 == 0) n /= 2; else break; long long p = n; if (p % 2 == 0 || p % 5 == 0) return 0; long long phi = euler(p); int cnt = 0; for (long long i = 1; i * i <= phi; i++) if (phi % i == 0) { factor[cnt++] = i; factor[cnt++] = phi / i; } sort(factor, factor + cnt); for (int i = 0; i < cnt; i++) if (power(10, factor[i], p) == 1) return factor[i]; return 0; } int main() { // freopen("t.txt", "r", stdin); int t = 0; while (scanf("%lld", &n), n) { t++; printf("Case %d: ", t); printf("%lld\n", work()); } return 0; }