noip2005 循环 (高精度+模拟)

A1154. 循环
时间限制: 1.0s   内存限制: 256.0MB  
总提交次数: 221   AC次数: 40   平均分: 32.71
将本题分享到:
       
   
试题来源
  NOIP2005 普及组
问题描述
  乐乐是一个聪明而又勤奋好学的孩子。他总喜欢探求事物的规律。一天,他突然对数的正整数次幂产生了兴趣。
  众所周知,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 < 10 100)和k(1 <= k <= 100),n和k之间用一个空格隔开,表示要求n的正整数次幂的最后k位的循环长度。
输出格式
  t包括一行,这一行只包含一个整数,表示循环长度。如果循环不存在,输出-1。
样例输入
32 2
样例输出
4
数据规模和约定
  对于30%的数据,k <= 4;
  对于全部的数据,k <= 100。


解析:我直接举一个例子进行说明吧:111 3(由于1的循环长度就是1,所以我直接从末两位循环开始)

          我们来看111的末两位循环:

          111  -> 321 -> 631 -> 041 -> 551 -> 161 -> 871 -> 681 -> 591 -> 601 -> 711 

          711处末两位出现循环,循环长度为10,循环节为11-> ...-> 01

          现在我们再来看末三位循环:

          111->...->601 (10个数)

          711->...->201 (201就是601^2的末三位)

          311->...->801 (801就是601^3的末三位)

          911->...->401 (401就是601^4的末三位)

          511->...->001 (001就是601^5的末三位)

          111->...->601 (601就是601^6的末三位)  

          末三位的循环长度就是5*10=50;


          好了,现在来讲具体的做法,并假设我们现在求数字n的后k位循环。

          朴素的做法就是直接求n^2,n^3,n^4.。。。,并判断是否出现循环。但观察上面的演示例子,我们发现可以不必这样,以n=111为例,末两位循环节长度为10,即表示n与(n^10)*n的末两位是相同的。对于末三位的循环,肯定是(m*n^10),即m*(n^10)与n^10的末三位是相同的,m*(n^10)*n的末三位与n相同。 于是,计算末三位循环的时候,我们就直接将n^10作为第一个数,然后每次乘n^10,共乘5次,末三位出现循环,即m=5,所以末三位循环街长度就为5*10=50。

        

代码:

#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
using namespace std;

const int maxn=100;
int n,a[maxn+10],b[maxn+10];
int c[2][maxn+10],ans[maxn+10];
char s[maxn+10];

bool same(int p[],int q[],int x)
{
  for(int i=1;i<=x;i++)if(p[i]!=q[i])return 0;
  return 1;
}

void multi_1(int x)
{
  int i,last=0;
  for(i=1;i<=ans[0];i++)
    {
      ans[i]=ans[i]*x+last;
      last=ans[i]/10,ans[i]%=10;
	}
  if(last>0)ans[++ans[0]]=last;
}

void multi_2(int w[],int p[],int q[])
{
  int i,k,last=0;
  w[0]=min(p[0]+q[0]-1,n);
  for(k=1;k<=w[0];k++)
    {
      for(w[k]=last,i=1;i<=p[0];i++)
        if(k+1-i>=1 && k+1-i<=q[0])w[k]+=p[i]*q[k+1-i];
      last=w[k]/10,w[k]%=10;
    }
  if(last)w[++w[0]]=last;
}

int get(int x)
{
  memcpy(c[0],b,sizeof(b));
  for(int i=1;i<=10;i++)
    {
      multi_2(c[i%2],c[(i+1)%2],b);
      if(same(c[i%2],b,x))
	    {
	      multi_2(c[i%2],c[(i-1)%2],a);
	      if(!same(c[i%2],a,x))goto d1;
	      memcpy(b,c[(i-1)%2],sizeof(c[0]));
	      return i;
		}
	}
  d1:printf("-1\n");
  exit(0);
}

int main()
{
  int i;  scanf("%s%d",s,&n);
  a[0]=strlen(s),n=min(a[0],n);
  for(i=1;i<=n;i++)a[i]=s[a[0]-i]-'0';
  memcpy(b,a,sizeof(a)),ans[0]=1,ans[1]=1;
  for(i=1;i<=n;i++)multi_1(get(i));
  for(i=ans[0];i>=1;i--)printf("%d",ans[i]);
  return 0;
}


    评论 2
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值