AcWing 1296. 聪明的燕姿

原题链接

考察:dfs+约数

错误思路:

       倍数法求出所有约数,N要开到1e9必然MLE且TLE

正确思路:

       观察约数之和的式子 s = (1+p1+p12+p13+..)*(1+p2+p22+p23+...) ,要满足能够这种性质的数在1~2E9内较少.再继续观察,假设p全为2,(1+2)*(1+2+22)*(1+2+22+23)...当上面的指数到达7时,就已经达到了1e8的级别.也就是说组成s的项数较少,这代表我们可以用DFS.

for(枚举质数)
  for(枚举指数)
    if(s%枚举和==0) dfs(下一个); 

         但是注意到s=2e9,也就是答案可能在1~s-1之间,如果我们枚举质数,需要枚举到s/log(s)也就是1e8的级别.这代表我们还需要优化,可以发现我们要枚举的大质数,基本是在1+p的形式里,也就是当s很大的时候,如果特别讨论1+p可以避免枚举大质数.现在抛去s=1+p的形式,剩下的是s= 1+p+p2+...这里的p的指数至少为2,这代表我们可以枚举到√s范围的质数.一下就把109范围的质数压到105.此时的时间复杂度是10*100*√s<1e8

这题总结一下就是如果你没发现满足该性质的数很少基本没救

 1 #include <iostream>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 typedef long long LL;
 6 const int N = 100010;
 7 int prime[N],cnt,len,call[N];
 8 bool st[N];
 9 void GetPrime(int n)
10 {
11     for(int i=2;i<=n;i++)
12     {
13         if(!st[i]) prime[++cnt] = i;
14         for(int j=1;prime[j]<=n/i;j++)
15         {
16             st[i*prime[j]] = 1;
17             if(i%prime[j]==0) break;
18         }
19     }
20 }
21 bool IsPrime(int s)
22 {
23     if(s<N) return !st[s]; 
24     for(int i=1;prime[i]<=s/prime[i];i++)
25         if(s%prime[i]==0) return false;
26     return true;
27 }
28 void dfs(int last,int now,int s)
29 {
30     if(s==1)
31     {
32         call[++len] = now;
33         return;
34     }
35     if(s-1>prime[last]&&IsPrime(s-1)) dfs(last,now*(s-1),1);
36     for(int i=last+1;prime[i]<=s/prime[i];i++)
37     {
38         int p = prime[i];
39         for(int j=1+p,t=p;j<=s;t*=p,j+=t)
40             if(s%j==0) dfs(i,now*t,s/j);
41     }
42 }
43 int main()
44 {
45     GetPrime(N-1);
46     int n;
47     while(scanf("%d",&n)!=EOF)
48     {
49         len = 0;
50         dfs(0,1,n);
51         sort(call+1,call+len+1);
52         printf("%d\n",len);
53         for(int i=1;i<=len;i++) printf("%d ",call[i]);
54         if(len) printf("\n");
55     }
56     return 0;
57 }

 

posted @ 2021-02-27 23:21  acmloser  阅读(62)  评论(0编辑  收藏  举报