聪明的燕姿
聪明的燕姿
城市中人们总是拿着号码牌,不停寻找,不断匹配,可是谁也不知道自己等的那个人是谁。
可是燕姿不一样,燕姿知道自己等的人是谁,因为燕姿数学学得好!
燕姿发现了一个神奇的算法:假设自己的号码牌上写着数字 ,那么自己等的人手上的号码牌数字的所有正约数之和必定等于 。
所以燕姿总是拿着号码牌在地铁和人海找数字(喂!这样真的靠谱吗)。
可是她忙着唱《绿光》,想拜托你写一个程序能够快速地找到所有自己等的人。
输入格式
输入包含 组数据。
对于每组数据,输入包含一个号码牌 。
输出格式
对于每组数据,输出有两行。
第一行包含一个整数 ,表示有 个等的人。
第二行包含相应的 个数,表示所有等的人的号码牌。
注意:你输出的号码牌必须按照升序排列。
数据范围
,
输入样例:
42
输出样例:
3 20 26 41
解题思路
这里是该题的新题解,点击链接进行跳转:https://www.cnblogs.com/onlyblues/p/17206228.html
昨天被这道题卡了一天,很多的地方都理解不了。
另外,题目描述的明明是《遇见》好吧。建议改为“懒 惰 的 燕 姿”,不出专辑就算了,还要我帮她做数论题,关键是我还不会做。[doge]
题目就是问有多少个数的约数和恰好等于。
根据唯一分解定理,一个数可以唯一分解成若干个素数的乘积形式,,其中为素数,且。
首先一个数所有约数的个数公式:。
一个数所有约数的和公式:。
首先可以发现我们要找的数一定是小于的,如果有,因为和都是的约数,因此仅看这两个约数的和已经大于了,因此要找的数一定小于。
因此我们要的数的约数和应该满足这种形式:
然后我们看看约数和公式的每一项,粗略估计一下一共有多少项。
可以发现通过粗略的估计,当枚举到素数时还是小于的,当枚举到素数时已经大于了,因此可以发现一共不会超过项,所以可以用dfs来爆搜每一个和,递归的深度不会超过。
另外一个问题是,如果我们在每一层枚举时直接从枚举到,时间复杂度就很高了,因此需要剪支。
可以证明对于等式,如果有,那么一定只会是这个形式。补充:因为对于每一层的,其实我们都是在搜,然后用除以这一项,得到,下一层就是搜索这个值。
分情况讨论:
如果分解质因数后是这种形式:,即只有一种质因子。反证法,假设的次幂至少,又因为,因此光是前几项的和就有,矛盾,因此必然是有。
如果如果分解质因数后是这种形式:(上面证明每一项的次幂必然小于),又因为我们规定,同时有,因此光看前两项的乘积就有,矛盾,因此必然是有。
再证明唯一性。假设存在两个,且,根据上面的证明,一定会有。因为,所以有,即,这就与矛盾了,因此唯一性得到证明,即如果存在一个且满足,那么有且仅有这一个。
因此结论是,如果某个数质因数分解后,第一项中的质数,那么的形式一定会是,且的有且只有这一个。
所以我们在每一层搜索第一项的时候,因为的最多只会有一个,且满足这个形式,所以对于这种情况只需要进行特判一下是否满足该形式就可以了。即便是存在满足上一种情况,还是有可能存在,该质数的若干个次幂和即可以整除,因此剩下的遍历范围不超过就可以了,看看是否存在有的项,满足整除的情况。
另外一些细节会在注释中补充,AC代码如下:
1 #include <cstdio> 2 #include <algorithm> 3 using namespace std; 4 5 const int N = 1e5 + 10; 6 7 int primes[N], cnt; 8 bool vis[N]; 9 int ans[N], sz; 10 11 void get_prime(int n) { 12 for (int i = 2; i <= n; i++) { 13 if (!vis[i]) primes[cnt++] = i; 14 for (int j = 0; primes[j] <= n / i; j++) { 15 vis[primes[j] * i] = true; 16 if (i % primes[j] == 0) break; 17 } 18 } 19 } 20 21 bool is_prime(int n) { 22 if (n < N) return !vis[n]; 23 for (int i = 0; primes[i] <= n / primes[i]; i++) { 24 if (n % primes[i] == 0) return false; 25 } 26 27 return true; 28 } 29 30 // cur表示当前要从哪个质数的下标开始枚举,prod表示要找的答案的部分质数乘积,s表示当前要分解约数和 31 void dfs(int cur, int prod, int s) { 32 if (s == 1) { // s == 1表示约数和已经分解完了,每一个Pi和αi已经找到 33 ans[sz++] = prod; // 答案就是各项Pi^αi的乘积 34 return; 35 } 36 37 // 先特判一下是否存在一个Pi >= sqrt(s),满足s == 1 + Pi -> Pi == s - 1 38 // 如果满足,等价于Pi应该大于等于当前枚举的质数,即s - 1 >= primes[cur],且Pi要是一个质数 39 // 不return是因为Pi >= sqrt(s)只是我们枚举的一种情况而已,接下来还要枚举Pi < sqrt(s)的情况 40 if (s - 1 >= primes[cur] && is_prime(s - 1)) ans[sz++] = prod * (s - 1); 41 42 // 特判后,Pi的枚举范围就缩小到Pi^2 < s 43 for (int i = cur; primes[i] < s / primes[i]; i++) { 44 int t = primes[i]; 45 // j == 1 + Pi^1 + Pi^2 + ... + Pi^αi 46 for (int j = 1 + primes[i]; j <= s; t *= primes[i], j += t) { 47 if (s % j == 0) dfs(i + 1, prod * t, s / j); // 只有j是s的某一项,即j可以整除s,即s % j == 0才可以继续往下搜 48 } 49 } 50 } 51 52 int main() { 53 get_prime(N - 1); // 筛质数,我们用到的质数小于sqrt(2e9) 54 55 int n; 56 while (~scanf("%d", &n)) { 57 sz = 0; 58 dfs(0, 1, n); 59 60 printf("%d\n", sz); 61 if (sz) { 62 sort(ans, ans + sz); 63 for (int i = 0; i < sz; i++) { 64 printf("%d ", ans[i]); 65 } 66 printf("\n"); 67 } 68 } 69 70 return 0; 71 }
参考资料
AcWing 1296. 聪明的燕姿(蓝桥杯C++ AB组辅导课):https://www.acwing.com/video/746/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/15966022.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探