这道题让我认识到,有些题你可以想到方法,但是在编程的时候如果没有把时间和空间复杂度仔细考虑从以下,是绝对不是优质的程序的。

    对于这道题,方法很好理解,就是长度为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;
}

 

posted on 2013-05-31 18:41  一路狂奔的人  阅读(332)  评论(2编辑  收藏  举报