Files
python/TangDou/CSP-J/AcWingTraining/T6/424.md
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

154 lines
6.2 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## [$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 <bits/stdc++.h>
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<K的限制
for (int i = 0, t = 0; i < K; i++) {
t += c[i]; // 进位与当前位置的和
a[i] = t % 10; // 余数保留下来
t /= 10; // 进位
}
}
// 输出高精度结果
void print(int a[]) {
int i = N - 1;
while (i > 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;
}
```