NOIP2005循环-题解

NOIP2005循环-题解

这个题目是NOIP2005的第四题,还是很不错的。

乐乐是一个聪明而又勤奋好学的孩子。他总喜欢探求事物的规律。
一天,他突然对数的正整数次幂产生了兴趣。
众所周知,2的正整数次幂最后一位数总是不断的在重复2,4,8,6,2,4,8,6……
我们说2的正整数次幂最后一位的循环长度是4(实际上4的倍数都可以说是循环长度,
但我们只考虑最小的循环长度)。类似的,其余的数字的正整数次幂最后一位数也有类似的循环现象。
这时乐乐的问题就出来了:是不是只有最后一位才有这样的循环呢?对于一个整数n的正整数次幂来说,
它的后k位是否会发生循环?如果循环的话,循环长度是多少呢?
注意:
1.如果n的某个正整数次幂的位数不足k,那么不足的高位看做是0。
2.如果循环长度是L,那么说明对于任意的正整数a,n的a次幂和a + L次幂的最后k位都相同。

首先我们最直接的方法就是对其进行直接的暴力求解,求其n次方,然后判断是否相等。

void brute_force(){
    
    
    int last_n;
//    读入数据
    scanf("%s %d",buffer, &last_n);
    raw.len = strlen(buffer);
    LEN = last_n + 2;
//    截取数据,并且低位在下。
    for (int i = 0; i < last_n; ++i) raw.d[i] = buffer[raw.len - i - 1] - '0';
    raw.len = last_n;
    memcpy(var.d,raw.d,sizeof(int)*MAXN);
    var.len = raw.len;
    for (int i = 1; i < 1000; ++i) {
    
    
        mul_2(&var, &raw, &var);
        if(equal(&var,&raw,raw.len)){
    
    
            printf("%d\n",i);
            return;
        }
    }
    printf("-1\n");
}

但是这样子计算量过大,同时会超过数据范围。

所以我们要重新设计我们的大数乘法:

typedef struct BI{
    
    
    int d[MAXN];
    int len;
} BI, *pbi;
BI raw, ans, var, temp, factor;
// raw 指的是原始的数据,不能被改动,ans是我们的结果
// var 变化变量,迭代时的临时结果存在这里面
// temp 用于运算时的临时变量
// factor 是我们的因数,我们会发生改变,自乘的因数
// ans 结果,表示我们的循环节

int equal(pbi a, pbi b, int n); // 只有再brute force下使用,我们后面不要使用
int mul(pbi a, pbi res, int n); // res = a*n
int mul_2(pbi a, pbi b, pbi res); // res = a*b
int _pow(pbi a, pbi res, int n); // res = a^n

但是我们又会发现,会有超时的问题,因为我们的大数的循环节很长,会超出我们的判断范围。

不难有,我们后面n位循环节一定是n-1位循环节的倍数。不然,对于其他的循环次数,我们肯定没有办法使得后面n-1保持不变,则:

loop[i] = loop[i-1]*x;

factor = n^loop[i];

// 当我们使用factor * n,得到的结果就会是后面i位不变

于是,我们在暴力判断的时候,就可以有:

for k=1 k<11 K++:
    n *= n^(loop[i-1]);
    if(new n == old n)
        loop[i] = loop[i-1]*k;
        break;

最后loop[i],就是我们的结果了。这里同时要注意,我们只要判断第i+1位有没有发生重复,也就是我们在10次循环内,一定会发生重复,因为我们只有10个数字,如果超出了,那么就循环节不存在。

replace equel(a,b,n) => a[n-1] == b[n-1]

对于第一位,我们可以直接用打表来直接得到。

同时我们可以针对中间过程进行优化:

扫描二维码关注公众号,回复: 15384255 查看本文章
for factor[i] = n^loop[i]

replace factor = n^loop[i] => factor = factor^k;

n *= factor;

replace ans = loop[0] => ans *= k;

上代码:

//
// Created by Zza on 2022/10/11.
//

#include "string.h"
#include "stdio.h"
#define MAXN 1024
int LEN = 100;
//const int MAXN = 1024;
// 首位打表
int first[] = {
    
    1, 1, 4,4,2,1,1,4,4,2};
char buffer[MAXN];
typedef struct BI{
    
    
    int d[MAXN];
    int len;
} BI, *pbi;
BI raw, ans, var, temp, factor;
// raw 指的是原始的数据,不能被改动,ans是我们的结果
// var 变化变量,迭代时的临时结果存在这里面
// temp 用于运算时的临时变量
// factor 是我们的因数,我们会发生改变,自乘的因数
// ans 结果,表示我们的循环节

int equal(pbi a, pbi b, int n); // 只有再brute force下使用,我们后面不要使用
int mul(pbi a, pbi res, int n); // res = a*n
int mul_2(pbi a, pbi b, pbi res); // res = a*b
int _pow(pbi a, pbi res, int n); // res = a^n

void disp(pbi tar);

void brute_force();

int get_loop(int n);
void calc_loop();

int main(){
    
    
//    FILE *fp;
//    fp = freopen(R"(D:\Coding\CppProjects\Algorithm_trainning\csp\NOIP\in.txt)", "r", stdin);

    calc_loop();
//    brute_force();
//    fclose(fp);
    return 0;
}
void calc_loop(){
    
    
    int last_n;
//    读入数据
    scanf("%s %d",buffer, &last_n);
    raw.len = strlen(buffer);
    LEN = last_n;
//    截取数据,并且低位在下。
    for (int i = 0; i < last_n; ++i) raw.d[i] = buffer[raw.len - i - 1] - '0';
    raw.len = last_n;
//    开始循环
    ans.d[0] = first[raw.d[0]]; ans.len = 1; // 第一位
    _pow(&raw, &factor, ans.d[0]);
//    disp(&ans);
    for (int i = 1; i < raw.len; ++i) {
    
    
        if(get_loop(i+1) == 0){
    
     printf("-1"); return;}
    }

//    计算结束,输出结果
    disp(&ans);

    return;
}

int get_loop(int n){
    
    
    int i;
    var.len = n;
    memcpy(var.d,raw.d,sizeof(int)*(n+1));
    for (i = 0; i < 10; ++i) {
    
    
        mul_2(&var, &factor, &var);
        if(raw.d[n-1] == var.d[n-1]){
    
    
            _pow(&factor, &var, i+1);
//            mul_2(&factor, &var, &factor);
            memcpy(&factor,&var,sizeof(BI));
            mul(&ans,&ans,i+1);
            return 1;
        }
    }
    return 0;
}

void brute_force(){
    
    
    int last_n;
//    读入数据
    scanf("%s %d",buffer, &last_n);
    raw.len = strlen(buffer);
    LEN = last_n + 2;
//    截取数据,并且低位在下。
    for (int i = 0; i < last_n; ++i) raw.d[i] = buffer[raw.len - i - 1] - '0';
    raw.len = last_n;
    memcpy(var.d,raw.d,sizeof(int)*MAXN);
    var.len = raw.len;
    for (int i = 1; i < 1000; ++i) {
    
    
        mul_2(&var, &raw, &var);
        if(equal(&var,&raw,raw.len)){
    
    
            printf("%d\n",i);
            return;
        }
    }
    printf("-1\n");
}

int equal(pbi a, pbi b, int n){
    
    
    for (int i = 0; i < n; ++i) {
    
    
        if(a->d[i] != b->d[i]) return 0;
    }
    return 1;
}

int mul(pbi a, pbi res, int n){
    
    
//    n <= 10
    int next = 0, l = a->len < LEN ? a->len : LEN;
    for (int i = 0; i < l; ++i) {
    
    
        temp.d[i] = a->d[i]*n;
        temp.d[i] += next;
        next = temp.d[i]/10;
        temp.d[i] %= 10;
    }
    temp.len = l;
    while (next){
    
    
        if(temp.len >= LEN)break;
        temp.d[temp.len] = next%10;
        next /= 10;
        temp.len++;
    }
    if(res){
    
    
        for (int i = 0; i < temp.len; ++i) {
    
    
            res->d[i] = temp.d[i];
        }
        res->len = temp.len;
    }
    return 1;
}

int mul_2(pbi a, pbi b, pbi res){
    
    
    int next = 0;
    memset(temp.d,0, sizeof(int)*MAXN);
    for (int i = 0; i < a->len; ++i) {
    
    
        for (int j = 0; j < b->len; ++j) {
    
    
            temp.d[i+j] += a->d[i]*b->d[j];
        }
    }
    temp.len = a->len+b->len-1 < LEN ? a->len+b->len-1 : LEN;
    for (int i = 0; i < temp.len; ++i) {
    
    
        temp.d[i] += next;
        next = temp.d[i]/10;
        temp.d[i] %= 10;
    }
    while (next){
    
    
        if(temp.len >= LEN)break;
        temp.d[temp.len] = next%10;
        next /= 10;
        temp.len++;
    }
    if(res){
    
    
        for (int i = 0; i < temp.len; ++i) {
    
    
            res->d[i] = temp.d[i];
        }
        res->len = temp.len;
    }
    return 1;
}

int _pow(pbi a, pbi res, int n){
    
    
    res->d[0] = 1;
    res->len = 1;
    for (int i = 0; i < n; ++i) {
    
    
        mul_2(res, a, res);
        if(res->len > LEN) res->len = LEN;
    }
    return 1;
}

void disp(pbi tar){
    
    
    for (int i = 0; i < tar->len; ++i) {
    
    
        printf("%d", tar->d[tar->len - 1 - i]);
    }
    printf("\n");
}


猜你喜欢

转载自blog.csdn.net/interval_package/article/details/127392521