icodelab 找朋友(P4397 [JLOI2014]聪明的燕姿)

描述

老师给每个同学一个号码牌,假设小明的号码牌上写着数字 S,那么其他那些手上的号码牌数字的所有正约数之和等于 S的同学就是小明的朋友。

输入

输入包含 k 组数据。 对于每组数据,输入包含一个数字S。

输出

对于每组数据,输出有两行,第一行包含一个整数 m,表示有 m 个小明的朋友。

第二行包含相应的 m 个数,表示小明朋友的手中的数字。

注意:你输出的数字必须按照升序排列。

输入样例 1

42

输出样例 1

3
20 26 41

提示

对于 100%的数据,k≤100, S≤2×10^9

思路

对于一个数 NN ,如果它的标准分解式为 N=p1a1p2a2p3a3…pnan 那么约数和

S=∏i=1n∑j=0aipijS=\begin{matrix} \prod_{i=1}^n \end{matrix}\begin{matrix} \sum_{j=0}^{a_i} {p_i}^j \end{matrix}( p1p_1 、 p2? 、… pn为质数)

因为 S<=2∗109S<=2*10^9,然后多试几组数据就会发现 NN, 、SS同级,所以 pi≤S{p_i}\leq\sqrt{S}

于是考虑暴搜,先筛出 ≤S\leq\sqrt{S}

? 的所有质数,先枚举 pi,对于每个 pip_i,枚举 aia_i暴搜,如果搜到S==1 的话,答案++

特别的,如果 S−1S-1为质数,且 S−1≥ 当前要搜的质数,答案也要++,此时的数为

已搜出的数*( S−1S-1)

代码:

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int N=1000010; 

long long q;
bool vis[N+5];
int m,flag=0,tot=0;
int a[(N+5)<<2],pr[N+5];

bool check(int x) {
	if(x==1)
		return 0;
	if(x<=N)
		return !vis[x];
	for(int i=1; pr[i]*pr[i]<=x; ++i)
		if(x%pr[i]==0) return 0;
	return 1;
}

void dfs(long long now,int x,long long y) {
	if(now==1) {
		a[++flag]=y;
		return;
	}
	if(now-1>=pr[x] && check(now-1))
		a[++flag]=y*(now-1);
	int i;
	long long p,tmp;
	for(i=x; pr[i]*pr[i]<=now; ++i) {
		tmp=pr[i];
		p=pr[i]+1;
		for(; p<=now; tmp*=pr[i],p+=tmp)
			if(now%p==0)
				dfs(now/p,i+1,y*tmp);
	}
	return;
}

int main() {
	for(int i=2; i<=N; i++) {
		if(!vis[i])
			pr[++tot]=i;
		for(int j=1; j<=tot&&i*pr[j]<=N; j++) {
			vis[i*pr[j]]=1;
			if(i%pr[j]==0)
				break;
		}
	}
	while(~scanf("%d",&m)) {
		q=sqrt(m);
		memset(a,0,sizeof(a));
		flag=0;
		dfs(1LL*m,1,1LL);
		printf("%d\n",flag);
		sort(a+1,a+flag+1);
		for(int i=1; i<flag; i++)
			printf("%d ",a[i]);
		if(flag)
			printf("%d\n",a[flag]);
	}
	return 0;
}

对于一个数NNN,如果它的标准分解式为N=p1a1p2a2p3a3…pnanN=p_1^{a_1}p_2^{a_2}p_3^{a_3}…p_n^{a_n}N=p1a1??p2a2??p3a3??…pnan?? 那么约数和

S=∏i=1n∑j=0aipijS=\begin{matrix} \prod_{i=1}^n \end{matrix}\begin{matrix} \sum_{j=0}^{a_i} {p_i}^j \end{matrix}S=∏i=1n??∑j=0ai??pi?j?(p1p_1p1?、p2p_2p2?、…pnp_npn?为质数)

因为S<=2∗109S<=2*10^9S<=2∗109,然后多试几组数据就会发现NNN、SSS同级,所以pi≤S{p_i}\leq\sqrt{S}pi?≤S

? 于是考虑暴搜,先筛出≤S\leq\sqrt{S}≤S

?的所有质数,先枚举 pip_ipi?,对于每个pip_ipi?,枚举aia_iai?暴搜,如果搜到S==1S==1S==1的话,答案+1

特别的,如果S−1S-1S−1为质数,且S−1≥S-1\geS−1≥当前要搜的质数,答案也要+1,此时的数为

已搜出的数*(S−1S-1S−1)

原文地址:https://www.cnblogs.com/mysh/p/11306299.html

时间: 2024-10-28 14:40:15

icodelab 找朋友(P4397 [JLOI2014]聪明的燕姿)的相关文章

P4397 [JLOI2014]聪明的燕姿

P4397 [JLOI2014]聪明的燕姿 题目背景 阴天傍晚车窗外 未来有一个人在等待 向左向右向前看 爱要拐几个弯才来 我遇见谁会有怎样的对白 我等的人他在多远的未来 我听见风来自地铁和人海 我排着队拿着爱的号码牌 题目描述 城市中人们总是拿着号码牌,不停寻找,不断匹配,可是谁也不知道自己等的那个人是谁. 可是燕姿不一样,燕姿知道自己等的人是谁,因为燕姿数学学得好!燕姿发现了一个神奇的算法:假设自己的号码牌上写着数字 \(S\),那么自己等的人手上的号码牌数字的所有正约数之和必定等于 \(S

bzoj3629 / P4397 [JLOI2014]聪明的燕姿

P4397 [JLOI2014]聪明的燕姿 根据唯一分解定理 $n=q_{1}^{p_{1}}*q_{2}^{p_{2}}*q_{3}^{p_{3}}*......*q_{m}^{p_{m}}$ 而$n$的约数和为$\prod_{i=1}^{m} \sum_{j=0}^{p_{i}}q_{i}^j$ 于是我们可以暴搜枚举每个约数的个数,而且只要枚举到$\sqrt{S}$ tips:注意最后一个数字后不带空格 1 #include<iostream> 2 #include<cstdio&g

bzoj 3629 [JLOI2014]聪明的燕姿(约数和,搜索)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3629 [题意] 给定S,找出所有约数和为S的数. [思路] 若n=p1^a1*p2^a2*...*pk^ak 则约数和f(n)为(p1^0+p1+p1^2+...+p1^a1)*(p2^0+p2+p2^2+...+p2^a2)*...*(pk^0+pk+pk^2+...+pk^ak) 考虑搜索,使得和为S.至于这个搜索怎么写的,我能说我看不懂吗=_= [代码] 1 #include<

BZOJ 3629 JLOI2014 聪明的燕姿 约数和+DFS

根据约数和公式来拆s,最后再把答案乘出来,我们发先这样的话递归层数不会太大每层枚举次数也不会太多,然而我们再来个剪枝就好了 #include<cstdio> #include<iostream> #include<cmath> #include<algorithm> #include<vector> using namespace std; inline int read() { int sum=0; char ch=getchar(); whi

bzoj 3629 [JLOI2014]聪明的燕姿——约数和定理+dfs

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3629 如果要搜索,肯定得质因数分解吧:就应该朝这个方向想. **约数和定理: 对于任意一个大于1的正整数N可以分解正整数:N=P?^a? P?^a?-Pn^an,则由约数个数定理可知N的正约数有(a?+1)(a?+1)(a?+1)-(an+1)个,那么N的(a?+1)(a?+1)(a?+1)-(an+1)个正约数的和为f(N)=(P?^0+P?^1+P?^2+-P?^a?)(P?^0+P?

bzoj 3629: [JLOI2014]聪明的燕姿【线性筛+dfs】

数论+爆搜 详见这位大佬https://blog.csdn.net/eolv99/article/details/39644419 #include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int N=100000; int s,p[N+5],tot,ans[N+5],con; bool v[N+5]; bool ok(int x) { if(x==1) retu

【BZOJ3629】【JLOI2014】聪明的燕姿 dfs 素数筛

链接: #include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/44698555"); } 题解: 我们发现把一个数分解质因数以后然后可以根据每种质因数的个数算出这个数的约数和.所以我们可以暴力拆解每个数,根号时间复杂度分解. 就是枚举每种质数它用了多少,然后这个数除一下再往下一层深搜. 代码: #

「JLOI2014」聪明的燕姿

传送门 Luogu 解题思路 很容易想到直接构造合法的数,但是这显然是会T飞的. 我们需要考虑这样一件事: 对于一个数 \(n\),对其进行质因数分解: \[n=\sum_{i=1}^x p_i^{c_i}\] 那么就会有: \[\sigma(n)=\prod_{i=1}^x \sum_{j=1}^{c_i}p^j\] 可以证明 \(\sigma(n)\) 和 \(n\) 同级,所以这个质因子 \(p_i\le \sqrt{S}\),所以我们可以直接爆搜出来所有小于 \(\sqrt{S}\) 的

「一本通 6.3 练习 2」聪明的燕姿

不会写,留坑... #include<bits/stdc++.h> using namespace std; const int N=44725; int s,p[N],ans[N]; bool vis[N]; void get_prime(){ for(int i=2;i<=N;++i){ if(!vis[i]) p[++p[0]]=i; for(int j=1;j<=p[0]&&i*p[j]<=N;++j){ vis[i*p[j]]=1; if(i%p[j