反素数

一开始做这道题http://codeforces.com/problemset/problem/27/E 才知道用反素数

之前之前完全不知道反素数是什么,那就从头开始学起

反素数

定义 

 官方定义:对于任何正整数,其约数个数记为,例如,如果某个正整数满足:对任意的正整

 数,都有,那么称为反素数。

 通俗来说,反素数就是在一个集合中因子最多并且值最小的数。

 //换一种说法,素数是只含本身和1两个因子的数,那么反素数,就是有很多因子的数。

特点

     

  解释:

 

  1. 反素数肯定是从2开始的连续素数的幂次形式的乘积。
  2. 数值小的素数的幂次大于等于数值大的素数,即n = p_{1}^{k_{1}}*p_{2}^{k_{2}}*p_{3}^{k_{3}}*...*p_{n-1}^{k_{n-1}}*p_{n}^{k_{n}}中,有k_{1}\geq k_{2}\geq k_{3}\geq ...\geq k_{n}

 1:如果不是从2开始的连续素数,那么如果幂次不变,把素数变成数值更小的素数,那么此时因子个数不变,但是n的

 数值变小了。交换到从2开始的连续素数的时候n值最小。

 2:如果数值小的素数的幂次小于数值大的素数的幂,那么如果把这两个素数交换位置(幂次不变),那么所得的n因子数量不变,    但是n的值变小,直到符合条件

 //我不是很理解这两条特点,但是先记着嘛,为了做题而做题

 模板

首先,既然要求因子数,首先想到的就是素因子分解。把n分解成n = p_{1}^{k_{1}}*p_{2}^{k_{2}}*p_{3}^{k_{3}}*...*p_{n-1}^{k_{n-1}}*p_{n}^{k_{n}}的形式,其中p是素数,k为他的指数。这样的话(k_{1}+1)*(k_{2}+1)*(k_{3}+1)*...*(k_{n}+1)就是总因子个数。 

//depth:当前在枚举第几个素数
//num:当前因子数
//temp:当前因子数量为num的时候的数值
//up:上一个素数的幂,这次应该小于等于这个幂次
void dfs(int depth,int temp,int num,int up)
{
    if(num>n || depth>=16) return;  //如果因子数比n大 
    if(num==n && ans>temp)
    {
        ans=temp;         //ans事先赋值无穷大
        return;             //求最优ans 最小ans
    }
    for(int i=1;i<=up;i++)
    {
        if(num*(i+1)>n) break;  //如果搜索到此的因子数目超过了n,那么不符合条件
        dfs(depth+1,temp=temp*p[depth],num*(i+1),i); 
        //temp这样赋值是因为乘上了这个因子
        //num这样赋值是因为有特点2可知因子数这样改变
    }
}

几种常见题型

1

给定因子数,求满足因子数恰好等于这个数的最小数。

题目链接:http://codeforces.com/problemset/problem/27/E

题意:给一个数n,求一个最小的正整数,使得它的因子个数为n

#include<stdio.h>
#define ULL unsigned long long
#define INF ~0ULL
int p[16] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
 
ULL ans;
int n;
 
//depth:当前在枚举第几个素数
//num:当前因子数
//temp:当前因子数量为num的时候的数值
//up:上一个素数的幂,这次应该小于等于这个幂次
void dfs(int depth,int temp,int num,int up)
{
    if(num>n || depth>=16) return;
    if(num==n && ans>temp)
    {
        ans=temp;
        return;
    }
    for(int i=1;i<=up;i++)
    {
        //if(temp/p[depth]>ans) break;
        if(num*(i+1)>n) break;  //如果因子数超过了n 那么break 
		dfs(depth+1,temp=temp*p[depth],num*(i+1),i);
    }
}
 
 
int main(){
    while(scanf("%d",&n)!=EOF){
        ans=NF;
        dfs(0,1,1,64);
        printf("%d\n",ans);
    }
    return 0;
}
 

2

给定一个n,求n以内因子数最多的数。

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1562

较上一道题的变化是

ULL的变化 还有 dfs(0,1,1,60); 而不是 dfs(0,1,1,64);

测试发现 dfs(0,1,1,64)会超时,而 dfs(0,1,1,60); dfs(0,1,1,50);都AC

dfs退出的终止条件改变了,temp>n超过了范围就退出

要求得因子最多的数,用ans_sum控制

当两个数因子数目相同时,选择ans较小的那一个数

#include<cstdio>
#include<iostream>
#define ULL unsigned long long
int p[16]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
 
ULL ans,ans_num;
ULL n;
 
//depth:当前在枚举第几个素数
//num:当前因子数
//temp:当前因子数量为num的时候的数值
//up:上一个素数的幂,这次应该小于等于这个幂次
void dfs(int depth,ULL temp,ULL num,ULL up)
{
    if(temp>n || depth>=16) return;
    if(num>ans_num)
	//if(num==n && ans>temp)
    {
        ans=temp;
        ans_num=num;
    }
    //当因子个数相同时,取值最小的
    if(num==ans_num && ans>temp) ans=temp;
    for(int i=1;i<=up;i++)
    {
        //if(temp/p[depth]>ans) break;
        if(num*(i+1)>n) break;  //如果因子数超过了n 那么break 
		dfs(depth+1,temp=temp*p[depth],num*(i+1),i);
    }
    return;
}
 
 
int main(){
    while(scanf("%lld",&n)!=EOF){
        ans_num=0;
        dfs(0,1,1,60);
        printf("%lld\n",ans);
    }
    return 0;
}
 

 

3

给定区间,求区间内因子数最多的数。

http://acm.hdu.edu.cn/showproblem.php?pid=2521

这时有两个地方需要改动。

1.要在更新的时候加一个判断条件,即更新的数字是否在区间内。

2.由于这时的数字已经不是严格意义上的反素数,所以不再符合性质2。因为遵循这个条件不一定能枚举到区间所有的数字。

对于性质一,其实有没有都可以,因为没有两个连续不能分解的数字,一旦给定区间,肯定有能够分解的数字。

#include<cstdio>
#include<iostream>
#define ULL unsigned long long
#define INF ~0
 
int p[16] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
ULL n,m;
ULL ans,ans_num;//ans为m,n以内的最大反素数(会持续更新),ans_sum为ans的因子数。
 
 
void dfs(int depth,ULL temp,ULL num)
{
    if(depth>=7 || temp>n) return;
    if(temp>=m && num>ans_num)
    {
        ans=temp;
        ans_num=num;
    }
    if(num==ans_num && ans>temp && temp>= m) ans=temp;
    for(int i=1;;i++)
	{
        if(num*(i+1)>n) break;  //如果因子数超过了n 那么break 
        dfs(depth+1,temp*=p[depth],num*(i+1));
    }
    return;
}
 
 
int main(){
    int T;
    scanf("%d",&T);
    while(T--)
	{
        scanf("%lld %lld",&m,&n);
        if(m==n)
		{
            printf("%lld\n",m);
            continue;
        }
        ans=INF;
        ans_num=0;
        dfs(0,1,1);
        printf("%lld\n",ans);
    }
    return 0;
}


//

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cherish_lii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值