hdu 2197

地址:http://acm.hdu.edu.cn/showproblem.php?pid=2197

题意:中文。

mark:这题一看题感觉应该是简单题,但是想了挺久。

一开始看到是这种单输入单输出的,而且n达到10^9那么大,想了一下应该是公式,没啥好的思路,就果断打表,然后丢到oeis.org里,结果发现根本没公式。

后来思考了一下,因为每个串所有可能肯定是2^n,设{i1,i2...im}是n的所有因数,可知非本源串的个数是2^n - sum{f[i1],f[i2]...f[im]}。其中f[i]代表长度为i的非本源串个数。这样只要枚举一下递归就可以了。

犹豫了很久没写,总觉得复杂度很大。后来写完以后测了一下10^9,跑了4秒才出来。调了一下才发现,时间主要花费在重复计算,因为n的所有因数的因数组成一个集合,会重复算很多次。例如100000000里有因数2,100000000的一个因数50000000也有因数2,50000000的因数有很多也含有2,这样f[2]会被计算很多次!

解决的方法是算出所有因数的非本源串值存在dp数组里,用的时候如果算过了就直接取(本质上是记忆化搜索)。因为n的因数不会超过sqrt(n)*2个,也就是不会超过20000个,所以感觉复杂度还可以。

代码:

 1 # include <stdio.h>
 2 # include <string.h>
 3 
 4 
 5 int dp[2][10010] ;
 6 int n ;
 7 
 8 
 9 int qpow(int a, int b)
10 {
11     int r = 1 ;
12     while (b)
13     {
14         if (b&1) r = (r*a)%2008 ;
15         a = (a*a)%2008 ;
16         b >>= 1 ;
17     }
18     return r ;
19 }
20 
21 
22 int gao(int x)
23 {
24     int i, sum = 2, flag ;
25     int xx ;
26     if (x == 1 || x == 2) return 2 ;
27     
28     if (x <= 10000) flag = 0, xx = x ;
29     else flag = 1, xx = n/x ;
30     
31     if (dp[flag][xx]!=-1) return dp[flag][xx] ;
32     
33     for (i = 2 ; i*i <= x ; i++)
34     {
35         if (x % i == 0)
36         {
37             sum += gao(i) ;
38             if (i*i != x) sum += gao(x/i) ;
39         }
40         sum %= 2008 ;
41     }
42     return dp[flag][xx] = (qpow(2,x)-sum+2008)%2008 ;
43 }
44 
45 
46 int main ()
47 {
48     while (~scanf ("%d", &n))
49     {
50         memset (dp, -1, sizeof(dp)) ;
51         printf ("%d\n", gao(n)) ;
52     }
53     return 0 ;
54 }

 

posted @ 2013-05-24 10:13  Seraph2012  阅读(443)  评论(0编辑  收藏  举报