其他分享
首页 > 其他分享> > P1050 循环

P1050 循环

作者:互联网

循环

难度较高的 noip 普及组原题。

看到 \(n\leq 10^{100}\) 就 深知出题人的良心 知道要用高精。

可以证明每一位最多在上一位的基础上循环 \(10\) 次就能找到循环(可以利用这一性质判无解)

所以对于 30% 的数据 \(n\leq 4\) 直接 暴力+高精 即可,最坏时间复杂度为 \(O(10^4\times 100^2)=O(10^7)\)。

考虑优化,发现一个数的前 \(k\) 位循环前提是前 \(k-1\) 位循环,所以可以依次统计前 \(1\to k\) 位的答案递推。

对于每一位 \(i\) 只需要在 \(n^{ans[i-1]}\mod 10^i\) 的基础上至多循环 \(10\) 次即可。

最坏时间复杂度和暴力相同。

这里举个栗子:

当计算 12 的“循环长度”时,可利用个位数 2 的独特性质。

每次乘 以 12 的 4 次幂,以降低时间复杂度。利用这个思想,可得出递推公式。

再具体分析一个数据:

78910912 4 为例,我们知道有效位 k 为 4 位,因此,我们读入数据后只要保 留后四位“0912”即可。

对于 0912,它的一位数的循环情况如下: \(2\to 4\to 8\to 6\to 2\),这样它的循环次数为 4 次。

那么对于二位数 12 来说, 我们可以先计算出 12 的 4 次幂,作为下一次计算循环节的基数:

\(12^4\mod 100=36\) (取 2 位有效位)

计算二位数的循环节时,每次只要乘 36,它的循环情况如下:

\(12\to 32\to 52\to 72\to 92\to 12\) 从中不难发现,不同的只是首位,而后一位始终为 2,共循环 5 次。

同样的思路,计算三位数的基数为: \(912^{4\times 5}\mod 1000=176\),而三位数的循环情况为:

\(912\to 512\to 112\to 712\to 312\to 912\) 是循环 5 次,后两位也始终为 12。

计算四位数的基数为: \(0912^{4\times 5\times 5}\mod 10000=3376\) 而四位数的循环情况为:

\(0912\to 8912\to 6912\to 4912\to 2912\to 0912\) 是循环 5 次,后三位也始终为 912。

至此,我们可以计算出总的循环节为: \(4\times 5\times 5\times 5=500\)。

还要注意这里答案最大可以达到 \(10^{100}\),所以也需要高精统计。

代码实现如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 210
using namespace std;

int n,k;
int a[N],pre[N],last[N],now[N],ans[N];
//a[] 记录 n 的 1-k 位。
//pre[] 记录 last[] 的变化情况情况。
//last[] 记录循环节基数。
//now[] 记录当前乘积情况,将其与 a[] 比对可以得出是否循环。
//ans[] 记录答案,注意这里也是要用高精的。

int The[]={1,1,4,4,2,1,1,4,4,2};
char s[N];

void Mul(int x[],int y[]){
	int p[N];
	memset(p,0,sizeof(p));
	for(int i=1;i<=k;i++)
		for(int j=1;j<=k;j++){
			p[i+j-1]+=x[i]*y[j];
			p[i+j]+=p[i+j-1]/10;
			p[i+j-1]%=10;
		}
	for(int i=1;i<=k;i++) x[i]=p[i];
	return;
}

void Mul_(int x[],int y){
	int p[N];
	memset(p,0,sizeof(p));
	for(int i=1;i<=k;i++){
		p[i]+=x[i]*y;
		p[i+1]+=p[i]/10;
		p[i]%=10;
	}
	for(int i=1;i<=k;i++) x[i]=p[i];
	return;
}

void Merge(int x[],int y[]){for(int i=1;i<=k;i++) x[i]=y[i];return;}

void init(){
	scanf("%s",s+1);n=strlen(s+1);
	scanf("%d",&k);
	for(int i=n;i>=n-k+1;i--) a[n-i+1]=s[i]-'0';
	Merge(last,a);
	ans[1]=The[a[1]];
	for(int i=1;i<ans[1];i++) Mul(last,a);
	Merge(pre,last);
	return;
}

void work(){
	int tot=1;
	while(++tot<=k){
		Merge(now,a);
		int cnt=0;
		while(++cnt<=10){
			Mul(now,last);
			if(now[tot]==a[tot]) break;
			Mul(pre,last);
		}
		if(cnt==11){puts("-1");return;}
		Merge(last,pre);
		Mul_(ans,cnt);
	}
	int len=110;
	while(!ans[len] && len>1) --len;
	for(int i=len;i>=1;i--) printf("%d",ans[i]);
	puts("");
	return;
}

int main(){
	init();
	work();
	return 0;
}

完结撒花。

标签:10,12,int,times,P1050,循环,0912
来源: https://www.cnblogs.com/lpf-666/p/14017367.html