反素数 -- 数学

反素数就是区间内约数个数最多的那个数。

在ACM题目里,

一般是求约数最多而且数字最小的那个数,【1--n】

二是求约数刚好等于n的最小的那个数

三是求区间里的最小反素数【beign,end】

1和3有区别吗?有,1可以加速,3只能暴力

先说下思路

思路 : 官方题解 :

(1)此题最容易想到的是穷举,但是肯定超时。

(2)我们可以知道,计算约数的个数和质因数分解有着很大的联系: 若Q的质因数分解为:Q=p1^k1*p2^k2*…*pm^km(p1…pm为素数,k1…km≥1),则Q有(k1+1)(k2+1)…(km+1)个约数。但是质因数分解的时间复杂度很高,所以也会超时。

(3)通过以上的公式,我们可以“突发奇想”:为何不能把质因数分解的过程反过来呢? 这个算法就是枚举每一个素数。初始时让m=1,然后从最小的素数2开始枚举,枚举因子中包含0个2、1个2、2个2…k个2,直至m*2^k大于区间的上限N。在这个基础上枚举3、5、7……的情况,算出现在已经得到的m的约数个数,同时与原有的记录进行比较和替换。直至所有的情况都被判定过了。 这个算法的优化:如果p1*p2*p3*……*pk>N(pi表示第i个素数),那么只要枚举到p k-1,既不浪费时间,也不会遗漏。

(4)以上的算法还不是最好的,还可以继续优化。 我们看以下的例子: 6=2*3 10=2*5 6和10的质因数分解“模式”完全相同,所以它们的约数个数是相同的。但是由于3<5,所以6<10。 12=2^2*3 18=3^2*2 12和18的质因数分解“模式”完全相同,所以它们的约数个数是相同的。但是由于12的质因数分解中2的指数大于3的指数,18的质因数分解中3的指数大于2的指数,所以12<18。 根据以上的举例,我们也可以对(3)中的算法进行一个改进:可以在枚举时进行一个优化,使得枚举到的数字中2的指数不小于3的指数,3的指数不小于5的指数……这样我们就能够得到质因数分解“模式”相同的最小数(证明略)。再对于每一个得到的数进行比较和记录。这个算法的优化力度极大,效率几乎达到了极限。

每个思路都很好理解,所以

http://codeforces.com/problemset/problem/27/E

这是签到题了,约数刚好等于n,那么最需dfs的时候判断即可,用第4这个方法的思路,time 30ms

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;

#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
LL pr, mx, BEGIN, END = 1e18;
const int maxn=50+20;
int prime[maxn];//这个记得用int,他保存的是质数,可以不用开maxn那么大
bool check[maxn];
int total;
int n;
void initprime() {
    for (int i=2; i<=maxn-20; i++) {
        if (!check[i]) { //是质数了
            prime[++total]=i;//只能这样记录,因为后面要用
        }
        for (int j=1; j<=total; j++) { //质数或者合数都进行的
            if (i*prime[j]>maxn-20) break;
            check[i*prime[j]]=1;
            if (i%prime[j]==0) break;
            //关键,使得它只被最小的质数筛去。例如i等于6的时候。
            //当时的质数只有2,3,5。6和2结合筛去了12,就break了
            //18留下等9的时候,9*2=18筛去
        }
    }
    return ;
}
void dfs(LL curnum, int cnt, int depth, int up) {
    if (depth > total) return ; // 越界了,用不到那么多素数
    if ((cnt > mx || cnt == mx && pr > curnum) && cnt == n) {
        pr = curnum;
        mx = cnt;
    }
    for (int i = 1; i <= up; ++i) { //枚举有多少个prime[depth]
        if (END / curnum < prime[depth]) return ;
        if ((BEGIN - 1) / curnum == END / curnum) return ; //区间不存在这个数的倍数
        curnum *= prime[depth]; //一路连乘上去
        dfs(curnum, cnt * (i + 1), depth + 1, i); // 2^2 * 3, 3最多2个
    }
}

void work() {
    cin >> n;
    dfs(1, 1, 1, 64);
    cout << pr << endl;
    return ;
}

int main() {
#ifdef local
    freopen("data.txt","r",stdin);
#endif
    initprime();
    work();
    return 0;
}

http://vjudge.net/problem/11177

求1--n里最小反素数,思路一样

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;

#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
LL pr, mx, BEGIN, END;
const int maxn=50+20;
int prime[maxn];
bool check[maxn];
int total;
void initprime() {
    for (int i=2; i<=maxn-20; i++) {
        if (!check[i]) {
            prime[++total]=i;
        }
        for (int j=1; j<=total; j++) {
            if (i*prime[j]>maxn-20) break;
            check[i*prime[j]]=1;
            if (i%prime[j]==0) break;
        }
    }
    return ;
}

void dfs(LL curnum, int cnt, int depth, int up) {
    if (depth > total) return ; //
    if ((cnt > mx || cnt == mx && pr > curnum) && curnum >= BEGIN) {
        pr = curnum;
        mx = cnt;
    }
    for (int i = 1; i <= up; ++i) {
        if (END / curnum < prime[depth]) return ;
        if ((BEGIN - 1) / curnum == END / curnum) return ; //
        curnum *= prime[depth]; //
        dfs(curnum, cnt * (i + 1), depth + 1, i);
    }
}

void work() {
    pr = 0, mx = 0, BEGIN = 1;
    cin >> END;
    dfs(1, 1, 1, 64);
    cout << pr << " " << mx << endl;
    return ;
}

int main() {
#ifdef local
    freopen("data.txt","r",stdin);
#endif
    initprime();
    int t;
    cin >> t;
    while (t--) {
        work();
    }
    return 0;
}

难题是这个

http://acm.njupt.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=1203

求解区间内[begin,end]最小反素数

用4思路啊,不行,为啥,因为4只关心约数个数,没考虑过区间问题,很明显2^2*3  4的思路限制了3的个数最多只能是2,

举个例子[150,150]你怎么破?150 = 2*3*5*5,这种情况,在4中是不可能的,所以只能用3了

首先,数据范围是n<=1e9,数据太大,如何快速算出来呢?我们注意到,如果是暴力算的话,最快的方法就是分解质因子,然后组合式计算啦。但是在算18和30的约数的时候,他们的

gcd(18,30)=6,其实是被重复算了的,那么我们思维反过来一下,把分解质因数变成用质因数去组合,使得变成区间内的数,这样一来,我们在2*3的时候,*3就得到了18,*5就得到了30,能省掉一定的时间。但是还是会TLE。假如我们现在枚举到的数是now,会不会它的约数根本就没可能存在于区间里呢?也就是[begin,end]根本就没这些约数。[7,11]内是不会存在6的倍数的。如果[1,begin-1]中6的约数和[1,end]中6的约数相同,说明什么?新加进去的区间[begin,end]根本就没6的约数,这里可以剪枝。还是TLE!!可行性剪枝,如果一个数是now,现在枚举一个新的质数去乘以它,去结合成新的数字,那么如果它无论组成什么其他数字,因子个数都不会超过当前最优值mx呢?怎么判断呢?放缩咯,假如现在是2*3,重新去匹配一个新的素数5,那么,我就要看,当前2*3还能再乘多少个3呢?我记作q,那么这个新的匹配,最理想的情况下因子个数会多2­­q­倍,为什么呢?把那些3,全部替换成5*7*11*13这样来算的话,就是有2q个了。别以为这样没用,当你搜[1,1e9]的时候,你枚举到8000w,再去枚举5那些是没用的,根本就不可能,这里能剪很多。

其实我们还有一个根本的问题没解决,那就是预处理素数到多大,还有万一它是大素数呢?

想着预处理多少,要看数据,预处理出来的最大质数,primeMax?2是要大过1e9才行的。为什么呢?因为你只有这样,才能防止它数据是两个大质数相乘的形式[primeMax2,primeMax2]。这里的因子个数是3,你枚举不到这个primeMax的话,就只能得到2。

还有那个大素数,没什么怕的,如果当前那个数now,幻想它乘以一个大质数,还是在end的范围的话,就看看*2和mx谁大咯。乘以一个大素数也才加一倍因子数。其实乘以一个小的质因子的话,因子数会更多,这里主要是判断只有一个大素数的特殊情况。枚举不到那个大素数那里的。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;

#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
LL pr, mx, BEGIN, END = 1e18;
const int maxn=1e6+20;
int prime[maxn];//这个记得用int,他保存的是质数,可以不用开maxn那么大
bool check[maxn];
int total;
int n;
void initprime() {
    for (int i=2; i<=maxn-20; i++) {
        if (!check[i]) { //是质数了
            prime[++total]=i;//只能这样记录,因为后面要用
        }
        for (int j=1; j<=total; j++) { //质数或者合数都进行的
            if (i*prime[j]>maxn-20) break;
            check[i*prime[j]]=1;
            if (i%prime[j]==0) break;
            //关键,使得它只被最小的质数筛去。例如i等于6的时候。
            //当时的质数只有2,3,5。6和2结合筛去了12,就break了
            //18留下等9的时候,9*2=18筛去
        }
    }
    return ;
}
LL mypow (LL a, LL b) {
    LL ans = 1;
    while (b) {
        if (b & 1) {
            ans *= a;
        }
        a *= a;
        b >>= 1;
    }
    return ans;
}
void dfs (int cur,int cnt,LL now,LL from) {
    LL t=from*(cnt+1);//现在一共拥有的因子数
    if (now>=BEGIN && t>mx || t==mx && pr>now && now >= BEGIN) { //有得换了
        mx=t;
        pr=now;
    }
    for (int i=cur; i<=total; ++i) { //枚举每一个素数
        LL temp = now*prime[i];
        if (END / now < prime[i]) return ; //这个数超出范围了
        if (i == cur) { //没有变,一直都是用这个数.2^k
            dfs(cur,cnt+1,temp,from);//唯一就是from没变,一直都是用着2,不是新质数
        } else { //枚举新质数了。
            LL k = (cnt+1)*from; //现在有K个因子
            LL q=(LL)(log(END/now) / log(prime[cur])); //2*3插入5时,用的是3来放缩
            LL add = k*mypow(2,q);
            if (add < mx) return ; //这里等于mx不return,可以输出minpr
            if ((BEGIN-1)/now == END/now) return; //不存在now的倍数
            if (END/now > prime[total]) { //试着给他乘上一个大素数 [999991,999991]
                if ( k*2 > mx ) { //乘以一个大素数,因子数*2
                    pr = END;//如果只有一个大素数[1e9+7,le9+7]那么,就是端点值
                    //否则,是2*3*5*bigprime的话,结果不是最优的,
                    mx = k*2;
                }
            }
            dfs(i,1,temp,k);
        }
    }
    return;
}

void work() {
    cin >> BEGIN >> END;
    dfs(1,0,1,1);
    cout << mx << endl;
//    cout << pr << " " << mx << endl;
    return ;
}

int main() {
#ifdef local
    freopen("data.txt","r",stdin);
#endif
    initprime();
    work();
    return 0;
}

//cur:当前枚举质数的下标,不用返回来枚举了。

//cnt:分解质因式时:拥有(当前下标那个)素数多少个

//now:当前枚举的那个数字,就是所有质因子相乘得到的数子

//from: 假如:2*2*3*5*7,然后枚举3,记录的是2*2,枚举5,记录的是2*2*3,

//如果是枚举相同的数,则不用变,因为它记录的是上一个不同的质因子一共拥有的因子数。

//所以乘上(cnt+1),就是包括上现在这个质因子一共拥有的因子数了。

dfs(1,0,1,1); //刚开始的时候,下标从1开始,拥有这个素数0个,当前数字最少也是1吧

时间: 2024-10-17 01:01:45

反素数 -- 数学的相关文章

Luogu P1463 [HAOI2007]反素数ant:数学 + dfs【反素数】

题目链接:https://www.luogu.org/problemnew/show/P1463 题意: 对于任何正整数x,其约数的个数记作g(x).例如g(1)=1.g(6)=4. 如果某个正整数x满足:g(x)>g(i) 0<i<x,则称x为反质数.例如,整数1,2,4,6等都是反质数. 现在给定一个数N,你能求出不超过N的最大的反质数么? 题解: 对于一个反素数p有两个结论: 若将p表示为 ∏(a[i]^k[i])的形式,其中a[i]为质因子,k[i]为指数. (1)a[i]为从2

luoguP1463 [HAOI2007]反素数

题目描述 对于任何正整数x,其约数的个数记作g(x).例如g(1)=1.g(6)=4. 如果某个正整数x满足:g(x)>g(i) 0<i<x,则称x为反质数.例如,整数1,2,4,6等都是反质数. 现在给定一个数N,你能求出不超过N的最大的反质数么? 输入输出格式 输入格式: 一个数N(1<=N<=2,000,000,000). 输出格式: 不超过N的最大的反质数. 输入输出样例 输入样例#1: 复制 1000 输出样例#1: 复制840 思路:一看这道题,不用说这是个数论,

反素数(暴力)

反素数 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 5194    Accepted Submission(s): 3043 Problem Description 反素数就是满足对于任意i(0<i<x),都有g(i)<g(x),(g(x)是x的因子个数),则x为一个反素数.现在给你一个整数区间[a,b],请你求出该区间的x

反素数深度分析

装载自:http://blog.csdn.net/ACdreamers/article/details/25049767 小知识点: 如果求约数的个数 756=2^2*3^3*7^1 (2+1)*(3+1)*(1+1)=24 基于上述结论,给出算法:按照质因数大小递增顺序搜索每一个质因子,枚举每一个质因子 为了剪枝: 性质一:一个反素数的质因子必然是从2开始连续的质数. 因为最多只需要10个素数构造:2,3,5,7,11,13,17,19,23,29 性质二:p=2^t1*3^t2*5^t3*7

poj 2886 Who Gets the Most Candies?(线段树+约瑟夫环+反素数)

Who Gets the Most Candies? Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 9934   Accepted: 3050 Case Time Limit: 2000MS Description N children are sitting in a circle to play a game. The children are numbered from 1 to N in clockwise o

【BZOJ 1053】 1053: [HAOI2007]反素数ant (反素数)

1053: [HAOI2007]反素数ant Description 对于任何正整数x,其约数的个数记作g(x).例如g(1)=1.g(6)=4.如果某个正整数x满足:g(x)>g(i) 0<i<x ,则称x为反质数.例如,整数1,2,4,6等都是反质数.现在给定一个数N,你能求出不超过N的最大的反质数么 ? Input 一个数N(1<=N<=2,000,000,000). Output 不超过N的最大的反质数. Sample Input 1000 Sample Output

[BZOJ 1053] [HAOI 2007]反素数ant

1053: [HAOI2007]反素数ant Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1857  Solved: 1034[Submit][Status][Discuss] Description 对于任何正整数x,其约数的个数记作g(x).例如g(1)=1.g(6)=4.如果某个正整数x满足:g(x)>g(i) 0<i<x,则称x为反质数.例如,整数1,2,4,6等都是反质数.现在给定一个数N,你能求出不超过N的最大的反质数么?

zoj2562--More Divisors(反素数模板)

More Divisors Time Limit: 2 Seconds      Memory Limit: 65536 KB Everybody knows that we use decimal notation, i.e. the base of our notation is 10. Historians say that it is so because men have ten fingers. Maybe they are right. However, this is often

zoj2562:搜索+数论(反素数)

题目大意:求n以内因子数量最多的数  n的范围为1e16 其实相当于求n以内最大的反素数... 由素数中的 算数基本原理 设d(a)为a的正因子的个数,则 d(n)=(a1+1)(a2+1).....*(an+1); 又由反素数的性质2: p=2^t1*3^t2*5^t3*7^t4.....必然t1>=t2>=t3>=.... 我们可以指定搜索策略和剪枝 详见代码和注释 #include <iostream> #include<stdio.h> #include&