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