## [$AcWing$ $424$. 循环](https://www.acwing.com/problem/content/426/) ### 一、题目描述 乐乐是一个聪明而又勤奋好学的孩子。 他总喜欢探求事物的规律。 一天,他突然对数的正整数次幂产生了兴趣。 众所周知,$2$ 的正整数次幂最后一位数总是不断的在重复 $2,4,8,6,2,4,8,6......$ 我们说 $2$ 的正整数次幂最后一位的循环长度是 $4$(实际上 $4$ 的倍数都可以说是循环长度,但我们只考虑最小的循环长度)。 类似的,其余的数字的正整数次幂最后一位数也有类似的循环现象: ![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/{year}/{month}/{md5}.{extName}/202309250859879.png) 这时乐乐的问题就出来了:是不是只有最后一位才有这样的循环呢?对于一个整数 $n$ 的正整数次幂来说,它的后 $k$ 位是否会发生循环?如果循环的话,循环长度是多少呢?  **注意**:  - 如果 $n$ 的某个正整数次幂的位数不足 $k$,那么不足的高位看做是 $0$。 - 如果循环长度是 $L$,那么说明对于任意的正整数 $a$,$n$ 的 $a$ 次幂和 $a+L$ 次幂的最后 $k$ 位都相同。 **输入格式** 输入文件只有一行,包含两个整数 $n$ 和 $k$,$n$ 和 $k$ 之间用一个空格隔开,表示要求 $n$ 的正整数次幂的最后 $k$ 位的循环长度。 **输出格式** 输出文件包括一行,这一行只包含一个整数,表示循环长度。 如果循环不存在,输出 `−1`。 **数据范围** $1≤n≤10100,1≤k≤100$ **输入样例**: ```cpp {.line-numbers} 32 2 ``` **输出样例**: ```cpp {.line-numbers} 4 ``` **举栗子**: $32^1=32 ,32^2=1024,32^3=32768,32^4=1048576$ $32^5=33554432,$ 此时发现后$2$位开始循环,而循环节的长度是$4$ ### 二、解题思路 **1、本题要求的是末 $k$ 位的循环节长度。** 需要从最低位开始判断: 假设一个数的末尾四位数字为 $1234$,要求 $1234$ 的循环节。 ```c++ ``` - ① 先判断最后一位 $4$ 的循环节,假设找到是$x$,即 $(1234)^x$ 的个位保持 $4$ 不变。 - ② 再找倒数第二位 $3$ 的循环节。为了保证每次循环最后一位都保持 $4$ 不变,故每一轮要以 $(1234)^x$ 为基准进行累乘。假设找到是 $y$,则说明 $((1234)^x)^y$ 的末两位保持 $34$ 不变。 - ③ 再找倒数第三位 $2$ 的循环节。为了保证每次循环末两位保持 $34$ 不变,故每一轮要以 $((1234)^x)^y$ 为基准进行累乘。假设找到是 $z$,则说明 $(((1234)^x)^y)^z$的末三位保持 $234$ 不变。 - ④再找倒数第四位 $1$ 的循环节。为了保证每次循环末三位保持 $234$ 不变,故每一轮要以 $(((1234)^x)^y)^z$ 为基准进行累乘。假设找到是 $r$,则说明 $((((1234)^x)^y)^z)^r$ 的末四位保持 $1234$ 不变。 故 $1234$ 的循环节为 $x ∗ y ∗ z ∗ r$ 将上述 $(1234)^x$,$((1234)^x)^y$,$(((1234)^x)^y)^z$,$((((1234)^x)^y)^z)^r$ 称为 **每一轮的累乘单位**。 **2、下表描述了基本判断过程:** ![](https://i-blog.csdnimg.cn/blog_migrate/f230f27bd718c51e2c8d6500338d8db0.png) ```cpp {.line-numbers} #include using namespace std; const int N = 110; int K; // 求最后K位的循环节长度是多少 int a[N]; // 原始数组 int b[N]; // 工作数组 int cnt[N] = {1}; // 次数,也就是答案 int st[10]; // 高精乘高精,限长K,结果保存到a数组中 void mul(int a[], int b[]) { int c[N] = {0}; // 临时数组 for (int i = 0; i < K; i++) // 只保留尾部K个,对应高精度数组,就是0~K-1 for (int j = 0; j < K; j++) if (i + j < K) c[i + j] += a[i] * b[j]; // 防止越界,需要加上i+j 0 && !a[i]) i--; // 去除前导零 while (i >= 0) cout << a[i--]; // 逐位输出 } int main() { string s; // 这里之所以用string类型,是为了照顾高精度同学 cin >> s >> K; // 求最后k个长度的循环节长度是多少 // 放入高精度计算数组,注意是倒着放的,下标从0开始 for (int i = s.size() - 1; i >= 0; i--) a[s.size() - 1 - i] = s[i] - '0'; for (int i = 0; i < K; i++) { // 执行K轮,从后往前,每轮固定一个尾部数字 memset(st, 0, sizeof st); // 某个数字是不是出现过,桶 memcpy(b, a, sizeof a); // b数组初始化 int j; for (j = 1; j <= 10; j++) { // 最多执行10次 mul(b, a); // 开始乘a // 对于高精度数组而言,头部即数字尾部 // 随着轮次的增加,我们希望检查的位置也发生了变化,位置下标为i // 如果当前计算的结果中,当前的计算位置,出现与原数字一样的数字,那么就是找到了循环节,此时的j就是我们想要收集的 if (b[i] == a[i]) break; // 如果某个数字重复出现,却一直没有和原始的一样,表示它自己的循环轨迹和原始的不一样,永远也不可以一样了,无解 if (st[b[i]]) { puts("-1"); exit(0); }; st[b[i]] = 1; // 记录出现过的数字 } // ① 累计次数 int c[N] = {j}; // 需要乘的次数,这里有用高精度乘高精度模拟高精度乘低精度 // 注意:即使j=10,上面的高精度乘法依然是有效的,因为在高精度乘法的内部采用的是模10进位的办法 mul(cnt, c); // 将次数累乘起来 // ② 变更乘数 memcpy(b, a, sizeof a); // 重新装载原始数字 for (int k = 1; k < j; k++) mul(b, a); // 反复乘j-1次 memcpy(a, b, sizeof b); // a升级了 } print(cnt); // 输出高精度数组内容 return 0; } ```