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 }