欧拉筛 优雅的筛筛筛

题目链接
http://47.110.142.74/contest/1692/problem/2
Description
定义函数f(x)为x的所有质因子乘积。例如:f(4)=2,f(6)=6。
注意:当x为1时,f(1)为1。
输入一个整数n,输出1到n的函数值。
Input
一个整数n(n≤1e6)
Output
n个整数,用空格分开
sample
Input
1
Output
1
Input
5
Output
1 2 3 2 5
题目分析
此题必须用到欧拉筛,此文会讲解欧拉筛。
即使用了欧拉筛也会超时!
因为此题目让你求一个数的所有质因数的乘积,最最最容易想到的就是 在prime数组中从最小的一个找起,验证x%prime【i】是否等于0,如果是就乘上。
这样太慢了,因为题目给出n,我们的工作是对1——n的每一个数都进行这样的操作。
于是 我们想到可以新建立一个数组solu其中储存
代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=1e6+7;
//欧拉筛 用最小的质因数去筛素数 
ll prime[maxn];//用来放素数 
ll num[maxn];//0一开始全是素数 
/*
1 1 1
2 2  2
3 3 3
4 2 2
5 5  5
6 2 3  6 
7 1 7 
8 2
9 3
10 2 5
*/
//优化版 
ll n,i,j,k,l,cou=1;
void oula()
{//全是素数
	for(i=2;i<=maxn;i++)
	{
		if(!num[i])prime[cou++]=i;//如果是素数,储存进prime数组 
		for(j=1;j<cou;j++)//在当前已有的数组中查找 
		{
			if(i*prime[j]>maxn)break;//超过范围了,退出 
			num[i*prime[j]]=1;//用最小的质数把所有素数的倍数删除 
			if(i%prime[j]==0)break; 
	/*eg.当i=6时,prime数组中已经存入了2 3 5
	6*2=12 12就被踢出来了
	6%2==0 假设此时不break
	j=2 prime【2】=3;
	6*3=18 18就被踢出来了 
	但是! 对于18而言,他的最小质因数是2 2*9=18 due to我们
	的i是从1开始循环直到n,所以说 i一定会有等于9的那一天
	此时j=1,prime【1】=2 2*9=18 18被踢出来了
	这就说明了一个问题 18会被筛掉两次 
	为了解决这个问题当i%prime【】当前素数==0,break,因为
	如果继续的话,就不是用i最小的质因数去筛了 
	 */ 
		}
	}
}
ll solu[maxn];
int main()
{
	ll n;
	cin>>n;	
	oula();
	for(i=1;i<=n;i++)
		solu[i]=1;//初始化为1,为后面的相乘做准备 
	for(i=1;i<cou;i++)
	{
		for(j=1;;j++)
		{
			if(prime[i]*j>n)
			break;
			solu[j*prime[i]]*=prime[i];
			//数组的下标j*prime【i】的因数肯定包括prime【i】
			//那我们solu数组是储存答案的 这个答案肯定是呈上prime【i】 
		}
	}
	for(i=1;i<=n;i++)
	{
		if(i==1)
		{
			cout<<"1 ";
			continue;
		}
		cout<<solu[i];
		if(i!=n)
		putchar(' ');
		}	
	
}

总结
欧拉筛的使用不用多说,原则就是用最小的质因数筛素数
值得一提的是本题怎么让他不超时
solu数组的使用非常关键
他的思想并不是像我们一开始那样,得到一个数n比如是9,我们下意识的反应从后往前推,找9的质因数,之后相乘,这个题因为是很多连续的数,我们可以换一种思想,从每一个质因数出发,让他去构造一个个的数,不用说,这个质数肯定是这个数的因子,那相乘不就是solu的数值吗。
希望得到斧正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值