154 lines
6.2 KiB
Markdown
154 lines
6.2 KiB
Markdown
|
|
## [$AcWing$ $424$. 循环](https://www.acwing.com/problem/content/426/)
|
|||
|
|
|
|||
|
|
### 一、题目描述
|
|||
|
|
乐乐是一个聪明而又勤奋好学的孩子。
|
|||
|
|
|
|||
|
|
他总喜欢探求事物的规律。
|
|||
|
|
|
|||
|
|
一天,他突然对数的正整数次幂产生了兴趣。
|
|||
|
|
|
|||
|
|
众所周知,$2$ 的正整数次幂最后一位数总是不断的在重复 $2,4,8,6,2,4,8,6......$
|
|||
|
|
我们说 $2$ 的正整数次幂最后一位的循环长度是 $4$(实际上 $4$ 的倍数都可以说是循环长度,但我们只考虑最小的循环长度)。
|
|||
|
|
|
|||
|
|
类似的,其余的数字的正整数次幂最后一位数也有类似的循环现象:
|
|||
|
|
|
|||
|
|

|
|||
|
|
|
|||
|
|
|
|||
|
|
这时乐乐的问题就出来了:是不是只有最后一位才有这样的循环呢?对于一个整数 $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、下表描述了基本判断过程:**
|
|||
|
|
|
|||
|
|

|
|||
|
|
|
|||
|
|
```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;
|
|||
|
|
}
|
|||
|
|
```
|