聪明的燕姿

聪明的燕姿

城市中人们总是拿着号码牌,不停寻找,不断匹配,可是谁也不知道自己等的那个人是谁。

可是燕姿不一样,燕姿知道自己等的人是谁,因为燕姿数学学得好!

燕姿发现了一个神奇的算法:假设自己的号码牌上写着数字 S,那么自己等的人手上的号码牌数字的所有正约数之和必定等于 S

所以燕姿总是拿着号码牌在地铁和人海找数字(喂!这样真的靠谱吗)。

可是她忙着唱《绿光》,想拜托你写一个程序能够快速地找到所有自己等的人。

输入格式

输入包含 k 组数据。

对于每组数据,输入包含一个号码牌 S

输出格式

对于每组数据,输出有两行。

第一行包含一个整数 m,表示有 m 个等的人。

第二行包含相应的 m 个数,表示所有等的人的号码牌。

注意:你输出的号码牌必须按照升序排列。

数据范围

1k100,
1S2×109

输入样例:

42

输出样例:

3
20 26 41

 

解题思路

  这里是该题的新题解,点击链接进行跳转:https://www.cnblogs.com/onlyblues/p/17206228.html

  昨天被这道题卡了一天,很多的地方都理解不了。

  另外,题目描述的明明是《遇见》好吧。建议改为“懒 惰 的 燕 姿”,不出专辑就算了,还要我帮她做数论题,关键是我还不会做。[doge]

  题目就是问有多少个数的约数和恰好等于s

  根据唯一分解定理,一个数可以唯一分解成若干个素数的乘积形式,N=P1α1P2α2Pnαn,其中Pi为素数,且αi>0,P1<P2<<Pn

  首先一个数所有约数的个数公式:(α1+1)(α2+1)(αn+1)

  一个数所有约数的和公式:(1+P1+P12++P1α1)(1+P2++P2α2)(1+Pn++Pnαn)

  首先可以发现我们要找的数一定是小于s的,如果有xs,因为1x都是x的约数,因此仅看这两个约数的和x+1已经大于s了,因此要找的数一定小于s

  因此我们要的数的约数和应该满足这种形式:s=(1+P1+P12++P1α1)(1+P2++P2α2)(1+Pn++Pnαn)

  然后我们看看约数和公式的每一项(1+Pi++Piαi),粗略估计一下一共有多少项(1+Pi++Piαi)

2×109=(1+P1+P12++P1α1)(1+P2++P2α2)(1+Pn++Pnαn)>(1+2)(1+3)(1+5)(1+7)(1+11)(1+13)(1+17)(1+19)(1+23)

  可以发现通过粗略的估计,当枚举到素数23时还是小于2×109的,当枚举到素数29时已经大于2×109了,因此可以发现(1+Pi++Piαi)一共不会超过10项,所以可以用dfs来爆搜每一个Piαi,递归的深度不会超过10

  另外一个问题是,如果我们在每一层枚举Pi时直接从2枚举到s,时间复杂度就很高了,因此需要剪支。

  可以证明对于等式s=(1+P1+P12++P1α1)(1+P2++P2α2)(1+Pn++Pnαn),如果有P1s,那么一定只会是s=1+P1这个形式。补充:因为对于每一层的s,其实我们都是在搜P1,然后用s除以这一项,得到s1+P1++P1α1,下一层就是搜索这个值。

  分情况讨论:

  如果s分解质因数后是这种形式:s=1+P1++P1α1,即只有一种质因子P1。反证法,假设P1的次幂至少2,又因为P1s,因此光是前几项的和就有1+P1+P12>s,矛盾,因此必然是有s=1+P1

  如果如果s分解质因数后是这种形式:s=(1+P1)(1+P2)(1+Pn)(上面证明每一项的次幂必然小于2),又因为我们规定P1<P2<<Pn,同时有P1s,因此光看前两项的乘积就有(1+P1)(1+P2)=1+P1+P2+P1×P2>P1×P2>s×s=s,矛盾,因此必然是有s=1+P1

  再证明唯一性。假设存在两个Pis,Pjs,且Pi<Pj,根据上面的证明,一定会有s=1+Pi,s=1+Pj。因为s=s,所以有1+Pi=1+Pj,即Pi=Pj,这就与Pi<Pj矛盾了,因此唯一性得到证明,即如果存在一个P1s且满足s=1+P1,那么有且仅有P1这一个。

  因此结论是,如果某个数s质因数分解后,第一项中的质数P1s,那么s的形式一定会是s=1+P1,且sP1有且只有这一个。

  所以我们在每一层搜索第一项(1+P1++P1α1)的时候,因为sP1最多只会有一个,且满足s=1+P1这个形式,所以对于这种情况只需要进行特判一下是否满足该形式就可以了。即便是存在P1s满足上一种情况,还是有可能存在P1<s,该质数的若干个次幂和即1+P1++P1α1可以整除s,因此剩下的遍历范围不超过s就可以了,看看是否存在有P1<s的项,满足整除s的情况。

  另外一些细节会在注释中补充,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/

posted @   onlyblues  阅读(208)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示