这道题让我认识到,有些题你可以想到方法,但是在编程的时候如果没有把时间和空间复杂度仔细考虑从以下,是绝对不是优质的程序的。
对于这道题,方法很好理解,就是长度为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; }