[洛谷 P1050] 循环 -- 数学 + 高精度

传送门:洛谷 P1050


题目描述

乐乐是一个聪明而又勤奋好学的孩子。他总喜欢探求事物的规律。一天,他突然对数的正整数次幂产生了兴趣。

众所周知, 2 2 2的正整数次幂最后一位数总是不断的在重复 2 , 4 , 8 , 6 , 2 , 4 , 8 , 6 … 2,4,8,6,2,4,8,6… 2,4,8,6,2,4,8,6我们说 2 2 2的正整数次幂最后一位的循环长度是 4 4 4(实际上 4 4 4的倍数都可以说是循环长度,但我们只考虑最小的循环长度)。类似的,其余的数字的正整数次幂最后一位数也有类似的循环现象:
n 循环 循环长度 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 \begin{array}{|c|c|c|} \hline n & \text{循环} & \text{循环长度} \\ \hline 2 & 2,4,8,6 & 4 \\ \hline 3 & 3,9,7,1 & 4\\ \hline 4 & 4,6 & 2\\ \hline 5 & 5 & 1\\ \hline 6 & 6 & 1\\ \hline 7 & 7,9,3,1 & 4\\ \hline 8 & 8,4,2,6 & 4\\ \hline 9 & 9,1 & 2\\ \hline \end{array} n23456789循环2,4,8,63,9,7,14,6567,9,3,18,4,2,69,1循环长度44211442

这时乐乐的问题就出来了:是不是只有最后一位才有这样的循环呢?对于一个整数nn的正整数次幂来说,它的后k位是否会发生循环?如果循环的话,循环长度是多少呢?

注意:
1. 如果 n n n的某个正整数次幂的位数不足k,那么不足的高位看做是 0 0 0
2. 如果循环长度是 L L L,那么说明对于任意的正整数 a , n a,n a,n a a a次幂和 a + L a+L a+L次幂的最后 k k k位都相同。


分析

一道头痛的凶残的数学题,首先看看数据范围,有点头痛(就不能好好地打打暴力么),加上高精度也绝对是要T了的。
 对此考虑用数学来补救
 在保证有解的情况下,若后 x x x位的循环长度为 y y y,则后 x + 1 x+1 x+1位的长度必定为 k × y k \times y k×y,其中 k ∈ N ∗ k \in N^* kN.因此,我们可以考虑从个位数开始逐步往前递推,每次只需求出对应的 k k k即可
 寻找 k k k:令原数为 n n n,若寻找后 x + 1 x+1 x+1的长度,我们只需要找到最小的 k k k,使之满足 n ≡ n × n y × k ( m o d    1 0 x + 1 ) n \equiv n \times n^{y \times k} (\mod 10^{x + 1}) nn×ny×k(mod10x+1),对此,先求出 n y n^y ny,然后枚举k判断即可
 判断有解:经证明(/逃,知道的 d a l a o dalao dalao,请留个言,谢谢), k ≤ 10 k \leq 10 k10,超出范围则无解


代码

#include <cstdio>
#include <cstdlib>
#include <cstring>

#define IL inline
//#define open(s) freopen(s".in","r",stdin); freopen(s".out","w",stdout);
//#define close fclose(stdin); fclose(stdout);

using namespace std;

IL int read()
{
    char c = getchar();
    int sum = 0 ,k = 1;
    for(;'0' > c || c > '9'; c = getchar())
        if(c == '-') k = -1;
    for(;'0' <= c && c <= '9'; c = getchar()) sum = sum * 10 + c - '0';
    return sum * k;
}

int m;
IL int min_(int x, int y) { return x < y ? x : y; }
IL int max_(int x, int y) { return x > y ? x : y; }

struct Bigint
{	
	int size;
	int num[105];
	
	IL Bigint()
	{
		size = 0;
		memset(num, 0, sizeof(num));
	}
	
	Bigint operator * (const Bigint &b) //高精*高精,限制了位数
	{
		Bigint c;
		for(int i = 1, p, r; i <= size; ++i)
		{
			p = i;
			r = 0;
			for(int j = 1; j <= b.size && p <= m; ++j, ++p)
			{
				r += c.num[p] + num[i] * b.num[j];
				c.num[p] = r % 10;
				r /= 10;
			}
			for(; p <= m && r; ++p, r /= 10)
			{
				r += c.num[p];
				c.num[p] = r % 10;
			}
		}
		c.size = min_(size + b.size, m);
		for(; !c.num[c.size]; --(c.size));
		return c;
	}
	
	Bigint operator * (const int b) //高精*单精,仅供ans使用
	{
		Bigint c;
		int s = size, r = 0;
		for(int i = 1; i <= size; ++i)
		{
			r += b * num[i];
			c.num[i] = r % 10;
			r /= 10;
		}
		for(; r; r /= 10)
			c.num[++s] = r % 10;
		c.size = s;
		return c;
	}
	
	IL bool is_same(int t, const Bigint &b)//判断后t位相等
	{
		for(int i = min_(t, max_(size, b.size)); i; --i)
		if(num[i] != b.num[i])
			return 0;
		return 1;
	}
	
	IL void wri()
	{
		for(int i = size; i; --i)
			printf("%d", num[i]);
		printf("\n");
	}
	
}k, ans;

char num[105];
int base[10] = {0, 1, 4, 4, 2, 1, 1, 4, 4, 2};//预处理个位情况

IL void power(int t)//好吧,简单的循环而已
{
	if(t == 1) return ;
	Bigint tmp = k;
	for(--t; t; --t) k = k * tmp;
}

IL void check(Bigint p, int len)//求后len位的解
{
	Bigint x = p;
	
	for(int t = 1; t <= 10; ++t)
	{
		x = x * k;
		if(p.is_same(len, x))
		{
			ans = ans * t;
			power(t);
			return ;
		}
	}
	ans.size = 1;
	ans.num[1] = -1;
}

int main()
{
    //open("circle")
    
    scanf(" %s %d", num + 1, &m);
    int len = strlen(num + 1);
    
    k.size = m < len ? m : len;
    
    for(int i = 1; i <= k.size; ++i)
    	k.num[i] = num[len - i + 1] - '0';
    
    Bigint p;
    
    p.size = 1; p.num[1] = num[len] - '0';
    ans.size = 1; ans.num[1] = base[num[len--] - '0'];

    power(ans.num[1]);
    
    for(int i = 2; i <= m; ++i)
    {
    	++(p.size);
    	if(len) p.num[p.size] = num[len--] - '0';
    	
    	check(p, i);
    	
    	if(ans.num[1] == -1) break;
    }
    
    ans.wri();
    
    //close
    return 0;
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值