zoj 1562 反素数 附上个人对反素数性质的证明

反素数的定义:对于不论什么正整数,其约数个数记为。比如,假设某个正整数满足:对随意的正整

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

从反素数的定义中能够看出两个性质:

(1)一个反素数的全部质因子必定是从2開始的连续若干个质数。由于反素数是保证约数个数为的这个数尽量小

(2)相同的道理,假设,那么必有

个人理解性证明:

对(1)如果不是从2開始,那么如果n的最小素因素是k,把k换成2,2的次数仍等于k的次数,得到N,可知,N<n,而且f(n)==f(N)。与n是反素数矛盾

对(2)如果ti<tj   ti,tj各自是是质因数i,j的次数,i<j。那么将i的次数换成tj,j的次数换成ti 能够得到N 。满足N<n且f(n)==f(N),与n是反素数矛盾

综上,两条性质得证

可能会疑惑,反素数的性质有个毛用!?

答案是:剪枝

先来一份TLE代码(2000ms+)

const int SIZE = 16;
const int MAXN = 65;
int p[SIZE] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};

ll n,ans,ansnum;
void dfs(int dep, ll tmp , ll mx)
{
    if(tmp<=n)
    {
        if(mx>ansnum)
        {
            ans=tmp;
            ansnum=mx;
        }
        if(mx==ansnum)ans=min(tmp,ans);///
    }
    if(tmp>=n || dep>SIZE-1)return;
    ll tt=1;
    for(int i=0;i<SIZE;i++)
    {
        if(n/tt<tmp)break;
        dfs(dep+1,tmp*tt,mx*(i+1));
        tt*=p[dep];
    }
}

int main()
{
    //IN("zoj1562.txt");
    while(~scanf("%lld",&n))
    {
        ans=ansnum=0;
        dfs(0,1,1);
        printf("%lld\n",ans);
    }
    return 0;
}

TLE的原因是dfs太多,剪枝方法非常easy。

1、DFS的时候,是从小的素数開始,由性质1,必须是次数从1開始而不是0開始。这样就剪下了一块

2、DFS的时候,深度为dep的那一层,循环处理的是prm[dep]的i次方,那么由性质2。其上限不能超过上一层的次数,又零零碎碎剪下了不少

另外注意两点:

1、由于害怕超过long long 所以用除法

if(n/tt<tmp)break;

2、注意求反素数这行代码:(是为了满足定义)

<pre code_snippet_id="457113" snippet_file_name="blog_20140824_2_8087056" name="code" class="cpp">if(mx==ansnum)ans=min(tmp,ans);

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <iostream>
#include <iomanip>
#include <cmath>
#include <map>
#include <set>
#include <queue>
using namespace std;

#define ls(rt) rt*2
#define rs(rt) rt*2+1
#define ll long long
#define ull unsigned long long
#define rep(i,s,e) for(int i=s;i<e;i++)
#define repe(i,s,e) for(int i=s;i<=e;i++)
#define CL(a,b) memset(a,b,sizeof(a))
#define IN(s) freopen(s,"r",stdin)
#define OUT(s) freopen(s,"w",stdout)
const ll ll_INF = ((ull)(-1))>>1;
const double EPS = 1e-8;
const int INF = 100000000;
const int SIZE = 16;
const int MAXN = 65;
int p[SIZE] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};

ll n,ans,ansnum;
void dfs(int dep, ll tmp , ll mx,int num)
{
    if(tmp<=n)
    {
        if(mx>ansnum)
        {
            ans=tmp;
            ansnum=mx;
        }
        if(mx==ansnum)ans=min(tmp,ans);///
    }
    if(tmp>=n || dep>SIZE-1)return;
    ll tt=1;
    for(int i=1;i<num;i++)
    {
        tt*=p[dep];
        if(n/tt<tmp)break;
        dfs(dep+1,tmp*tt,mx*(i+1),i+1);
    }
}

int main()
{
    //IN("zoj1562.txt");
    while(~scanf("%lld",&n))
    {
        ans=ansnum=0;
        dfs(0,1,1,MAXN);
        printf("%lld\n",ans);
    }
    return 0;
}
时间: 2024-08-03 11:02:05

zoj 1562 反素数 附上个人对反素数性质的证明的相关文章

zoj 2526 反素数 附上个人对反素数性质的证明

反素数的定义:对于任何正整数,其约数个数记为,例如,如果某个正整数满足:对任意的正整 数,都有,那么称为反素数. 从反素数的定义中可以看出两个性质: (1)一个反素数的所有质因子必然是从2开始的连续若干个质数,因为反素数是保证约数个数为的这个数尽量小 (2)同样的道理,如果,那么必有 个人理解性证明: 对(1)假设不是从2开始,那么假设n的最小素因素是k,把k换成2,2的次数仍等于k的次数,得到N,可知,N<n,并且f(n)==f(N),与n是反素数矛盾 对(2)假设ti<tj   ti,tj

ZOJ 1562 More Divisors 反素数

最裸的反素数问题.求不大于N的数约数最多的数是多少,如果有多个求最小值. 设x的约数个数为g(x),如果有某个正整数a有对于任意0<i<a有g(i)<g(a),则称a为反素数. g(x)的计算方法,先分解质因子x=a^b*c^d*e^f- g(x)=(b+1)*(d+1)*(f+1),即指数+1的乘积 反素数有性质: 一个反素数的质因子必然是从2开始的连续质数 2^t1*3^t2*5^t3*7^t4.....必然有t1>=t2>=t3>=.... 有了这些性质之后,就可

反调试手法之CreateProcess反调试

反调试手法之CreateProcess反调试 在学习Win32 创建进程的时候.我们发现了有一个进程信息结构体. STARTUPINFO. 这个结构体可以实现反调试. 具体CreateProcess可以参考上一篇博客.:  https://www.cnblogs.com/iBinary/p/9571577.html 一丶实现反调试需要的东西. 假设我们要实现反调试. 那么我们需要一个API GetStartupInfo (STARTUPINFO) 使用此API可以在进程启动的时候获取启动信息结构

判断101-200之间有多少个素数,并输出所有素数(C)

/* *题目:判断101-200之间有多少个素数,并输出所有素数. * *程序分析:判断素数的方法:用一个数分别去除2到sqrt(这个数),如果 *能被整除,则表明此数不是素数,反之是素数. */ #include<stdio.h> #include<math.h> int main() { int k,i,j,m,sum=0; int flag; for(i=101;i<=200;i++){ flag=1; k=sqrt(i); for(j=2;j<=k;j++){

判断101-200之间有多少个素数,并输出所有素数。

package com.mumu.ready;import static com.mumu.ready.Print.print; public class Prime {//题目:判断101-200之间有多少个素数,并输出所有素数. public static boolean prime(int n) { boolean flag=false; for(int i=2;i<=n/2;i++) { if(n%i==0) { flag=true; continue; } } return flag;

一般筛法求素数+快速线性筛法求素数

素数总是一个比较常涉及到的内容,掌握求素数的方法是一项基本功. 基本原则就是题目如果只需要判断少量数字是否为素数,直接枚举因子2 ..N^(0.5) ,看看能否整除N. 如果需要判断的次数较多,则先用下面介绍的办法预处理. 一般的线性筛法 首先先介绍一般的线性筛法求素数 void make_prime() { memset(prime, 1, sizeof(prime)); prime[0]=false; prime[1]=false; int N=31700; for (int i=2; i<

标记素数法(模板)+素数打表

#include <stdio.h> #include <string.h> #define N 3000000 int f[3000000]; int main() { memset(f, 0, sizeof(f)); int i, j; f[0]=1; f[1]=1; for(i=2; i<=N; i++) { if(f[i]==0) { for(j=i*2; j<=N; j+=i) { f[j]=1; //不是素数 } } } // 打印所有发f[i]==0, 即

判断101-200之间有多少个素数,并输出所有素数

题目:判断101-200之间有多少个素数,并输出所有素数. 程序分析:判断素数的方法:用一个数分别去除2得到sqrt(这个数),如果能被整除,则表明此数不是素数,反之是素数. 1 package com.li.FiftyAlgorthm; 2 3 /** 4 * 题目:判断101-200之间有多少个素数,并输出所有素数. 5 * 6 * 程序分析:判断素数的方法:用一个数分别去除2得到sqrt(这个数),如果能被整除,则表明此数不是素数,反之是素数. 7 * 8 * @author yejin

【转载】一般筛法求素数+快速线性筛法求素数

素数总是一个比较常涉及到的内容,掌握求素数的方法是一项基本功. 基本原则就是题目如果只需要判断少量数字是否为素数,直接枚举因子2 ..N^(0.5) ,看看能否整除N. 如果需要判断的次数较多,则先用下面介绍的办法预处理. 一般的线性筛法 首先先介绍一般的线性筛法求素数 void make_prime() { memset(prime, 1, sizeof(prime)); prime[0]=false; prime[1]=false; int N=31700; for (int i=2; i<