1. 程式人生 > >NOIP2005普及組第4題 迴圈(高精度)

NOIP2005普及組第4題 迴圈(高精度)

題目描述

樂樂是一個聰明而又勤奮好學的孩子。他總喜歡探求事物的規律。一天,他突然對數的正整數次冪產生了興趣。

眾所周知,2的正整數次冪最後一位數總是不斷的在重複2,4,8,6,2,4,8,6……我們說2的正整數次冪最後一位的迴圈長度是4(實際上4的倍數都可以說是迴圈長度,但我們只考慮最小的迴圈長度)。類似的,其餘的數字的正整數次冪最後一位數也有類似的迴圈現象:

迴圈

迴圈長度

2

2、4、8、6

4

3

3、9、7、1

4

4

4、6

2

5

5

1

6

6

1

7

7、9、3、1

4

8

8、4、2、6

4

9

9、1

2

這時樂樂的問題就出來了:是不是隻有最後一位才有這樣的迴圈呢?對於一個整數n的正整數次冪來說,它的後k位是否會發生迴圈?如果迴圈的話,迴圈長度是多少呢?

注意:

1. 如果n的某個正整數次冪的位數不足k,那麼不足的高位看做是0。

2. 如果迴圈長度是L,那麼說明對於任意的正整數a,n的a次冪和a + L次冪的最後k位都相同。

輸入

      只有一行,包含兩個整數n(1 <= n < 10100)和k(1 <= k <= 100),n和k之間用一個空格隔開,表示要求n的正整數次冪的最後k位的迴圈長度。

輸出

    包括一行,這一行只包含一個整數,表示迴圈長度。如果迴圈不存在,輸出-1。

【資料規模】

對於30%的資料,k <= 4;

對於全部的資料,k <= 100。


樣例輸入

32 2

樣例輸出

4

設後i位的迴圈節長度為 loop[i]

可以知道 loop[i] = x*loop[i-1],其中x為正整數且i>1,每次判斷只用判斷第i位是否與最初相同即可

就可以從後往前逐個遞推上去

判斷是否有迴圈,只需要標記一下求的那一位出現的數字,如果有一個不是最初的數字先出現了兩次則不可能有迴圈

如一個數a,先求出第一位數的迴圈長度為k,然後求第二位時每次就乘以a^k

a * a^k * a^k .. 直到第二位出現相同數字a^k 乘了p次,此時迴圈長度為 k*p,然後更新a^k到(a^k)^p 減少一點時間,不過直接算k*p次應該也可以吧。

最後的答案也是一個大數,longlong存不下,因為這個一直wa

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
typedef long long LL;
const int N = 1e2 + 10;
char s[N];
int k,cnt;
LL ans;
struct node{
    int num[N],len;
    node(){
        memset(num,0,sizeof num);
        len = 1;
    }
    node friend operator * (node a,node b){ //運算子過載,大數乘法
        node c;
        for(int i=1;i<=a.len;i++){
            for(int j=1;j<=b.len;j++){
                int pos = i+j-1,add = 0;
                if(pos>k) break;
                c.num[pos] += a.num[i] * b.num[j];
                add = c.num[pos]/10; c.num[pos] %= 10;
                while(add>0&&pos<=k){
                    pos++;
                    c.num[pos] += add;
                    add = c.num[pos]/10;
                    c.num[pos] %= 10;
                }
                if(pos>c.len) c.len = pos;
            }
        }
        return c;
    }
}a,st;
int flag = 0;
int slove(int x,node now){//求第x位迴圈長度,now為當前要乘上的基數
    int cnt = 1;
    node tem = st * now;
    int vis[15]; memset(vis,0,sizeof vis);
    while(tem.num[x]!=st.num[x]){
        if(vis[tem.num[x]]) return 0;//如果先兩次出現了別的數字,則不存在迴圈
        vis[tem.num[x]] = 1;
        tem = tem * now;
        cnt++;
    }
    return cnt;
}
int main()
{
    scanf("%s%d",s,&k);
    node ans; ans.len = 1;
    ans.num[1] = 1;
    int len = strlen(s);
    for(int i=1;i<=k&&i<=len;i++) a.num[i] = s[len-i]-'0';
    a.len = min(k, len);
    st = a;
    int flag = 0;
    for(int i=1;i<=k;i++){
        int mul = slove(i, a);
        if(mul==0){
            flag = 1;
            break;
        }
        node tem = a;
        for(int j=1;j<mul;j++) tem = tem * a;//得到新的要乘的基數
        a = tem;
        tem.len = 0;
        while(mul){
            tem.num[++tem.len] = mul % 10;
            mul /= 10;
        }
        ans = ans * tem;//更新當前位迴圈所需的最短迴圈長度
         
    }
    if(flag) printf("-1\n");
    else {
        for(int i=ans.len;i>=1;i--){
            printf("%d",ans.num[i]);
        }
        printf("\n");
    }
     
    return 0;
}