【P1050 [NOIP2005 普及组] 循环】
思路:
可以从小到大一位一位递推出前 kk 位的循环节长度。

假设已经求出前 k0k0 位的循环节长度 t0t0,那当我们求前 k0+1k0+1 位的循环节长度时,
只需枚举 n×nt0,n×n2t0,n×n3t0,…,n×n10t0n×nt0,n×n2t0,n×n3t0,…,n×n10t0 是否和 nn 相同即可。

注意这里只需要枚举前10项,因为前 k0k0 位是固定不变的,只有第 k0+1k0+1 位会变化,因此最多只有10种不同选择。

时间复杂度
总共需要递推 kk 位,每次递推时最多需要枚举10项,每次枚举时会用到高精度乘法。

这里高精度乘法没有用FFT加速,因此时间复杂度是 O(10k3)O(10k3)

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 110;

int m;
int nums[N], power[N];

void mul(int c[], int a[], int b[])
{
    static int temp[N];
    memset(temp, 0, sizeof temp);

    for (int i = 0; i < m; i ++ )
        for (int j = 0; j < m; j ++ )
            if (i + j < m)
                temp[i + j] += a[i] * b[j];
    for (int i = 0, t = 0; i < m; i ++ )
    {
        t += temp[i];
        temp[i] = t % 10;
        t /= 10;
    }
    memcpy(c, temp, sizeof temp);
}

void mul(int c[], int a[], int b)
{
    for (int i = 0, t = 0; i < m; i ++ )
    {
        t += a[i] * b;
        c[i] = t % 10;
        t /= 10;
    }
}

int main()
{
    string str;
    cin >> str >> m;
    for (int i = 0, j = str.size() - 1; j >= 0; j --, i ++ ) nums[i] = str[j] - '0';
    memcpy(power, nums, sizeof nums);

    int per[N] = {1};
    int p1[N], pn[N];   // p1 = 1 * p^(kt), pn = n * p^(kt)

    for (int k = 1; k <= m; k ++ )
    {
        memcpy(pn, nums, sizeof nums);
        memset(p1, 0, sizeof p1);
        p1[0] = 1;

        int r = 0;
        while (r <= 10)
        {
            mul(pn, pn, power);
            mul(p1, p1, power);
            r ++ ;
            if (pn[k - 1] == nums[k - 1]) break;
        }
        memcpy(power, p1, sizeof p1);

        if (r > 10)
        {
            memset(per, 0, sizeof per);
            per[0] = -1;
            break;
        }

        mul(per, per, r);
    }

    int k = m;
    while (!per[k]) k -- ;

    while (k >= 0) cout << per[k -- ];

    return 0;
}