这道题让我认识到,有些题你可以想到方法,但是在编程的时候如果没有把时间和空间复杂度仔细考虑从以下,是绝对不是优质的程序的。
对于这道题,方法很好理解,就是长度为n的本原串可以由从2开始的n的约数的本原串计算,计算式如下:F[n]=2^n-ΣF[i]-2;(其中,i是n的约数),
可以这么理解:长度n的串共有2^n,F[n]=2^n-非本原串;非本原串=长度是n的约数的本原串的个数+2(加2的原因是因为本原串没有全0和全1的情况,非本原串要加上)
长度是n的非本原串个数=ΣF[i](其中,i是n的约数,即可以由长度是i的子串构成长度是n的非本原串),仔细考虑这一点哟!
理解这个表达式以后,就是编程。在编程时我刚开始使用的是先将长度为1~100000000的本原串个数计算出来,存在数组里,当询问时我就查询就行,没想到速度很慢。
然后我参考大牛们的代码,发现用递归写,需要长度为多少的本原串时,再递归计算,计算后存储在数组中。这样的效率有很大提高。
但是,仅仅这样优化还是不够,需要将求2^n这个子函数进行优化,大牛们使用的是根据指数n的二进制形式求出2^n,挺巧妙地。达到了这两层优化,基本上就可以AC了。
下面是我的代码:
#include<stdio.h>
#define L 100000002
#define MOD 2008
int data[L]={0};
int n_mod(int n) //原来写的求2^n的函数
{
int i=1,result=1;
for(i=1;i<=n;i++)
{
result = (result << 1) % 2008;
}
return (result%2008);
}
int pow_m(int a,int n)
{
int ret=1;
int tmp=a%MOD;
while(n)
{
if(n&1)
{
ret*=tmp;
ret%=MOD;
}
tmp*=tmp;
tmp%=MOD;
n>>=1;
}
return ret;
}
int cal(int n)
{
if(data[n]!=0) return data[n];
int i;
data[n]=(pow_m(2,n)-2)%2008;
for(i=2;i*i<=n;i++)
{
if(i*i==n)
{
data[n]=(data[n]-cal(i))%2008;
}
else if(n%i==0)
{
data[n]=(data[n]-cal(i))%2008;
data[n]=(data[n]-cal(n/i))%2008;
}
}
return data[n]=(data[n]+2008)%2008;
}
int main()
{
int n;
data[0]=0;data[1]=2;data[2]=2;
while(scanf("%d",&n)==1)
{
printf("%d\n",cal(n));
}
return 1;
}
浙公网安备 33010602011771号