zoukankan      html  css  js  c++  java
  • luoguP1463 [POI2002][HAOI2007]反素数

    大意

    对于任何正整数 (x),其约数的个数记作 (g(x))。例如 (g(1)=1)(g(6)=4)

    如果某个正整数 (x) 满足:(0 lt i lt x),都有 (g(x) gt g(i)),则称 (x) 为反质数。例如,整数 (1,2,4,6) 等都是反质数。

    现在给定一个数 (N),求出不超过 (N) 的最大的反质数。

    题解

    参考了黄东旭李煜东的蓝书,主要因为证明比较严谨(并不是在说老姚不严谨)

    • 引理1
      1~(N) 中最大的反素数,就是 1~(N) 中约数最多的数中最小的那一个。

    证明:
    (x) 为 1~(N) 中约数最多的数中最小的数,那么有:
    (forall i, 0<i<x),有 (g(i)<g(x)),说明 (x) 是一个反素数。
    (forall i, x<i<N),有 (g(i) leq g(x)),说明大于 (x) 的都不是反素数。
    (x) 即为所求。

    • 引理2
      1~(N) 中任何数的不同的质因子都不会超过10个:因为最小的11个素数的乘积:(2 imes 3 imes 5 imes 7 imes 11 imes 13 imes 17 imes 19 imes 23 imes 29 imes 31) 超过了 (2 imes 10^9)
      1~(N) 中任何数的质因子的指数之和也不会超过30,因为即使是只有最小的质因子2,(2^{31}) 也超过了 (2 imes 10^9)。((2^{31}-1)是2147483647)

    • 引理3
      (forall xin [1,N])(x)为反素数的必要条件是 (x) 可分解成 (2^{c_1} imes 3^{c_2} imes 5^{c_3} imes ... imes 29^{c_{10}}),并且 (c_1 geq c_2 geq ... geq c_{10} geq 0),简单地说就是 (x) 的质因子都是连续的,并且指数不上升(其实也就是指数单调递减,之所以说不上升是考虑到存在后面几个质因子指数都为0的情况。)。

    首先为什么质因子必须是由小到大连续的,因为反素数的定义是与质因子的个数有关的,如果一个数分解出的质因子不连续,我们完全可以用空掉的那个因子替换掉后面的某个更大的因子,从而得到一个更小的数,而质因子的个数并不变,故该数不为反素数。举个例子:(84=2^2 imes 3 imes 7),中间缺少了(5),所以存在一个数(60=2^2 imes 3 imes 5),60小于84,但约数的个数却与84相等,可以证明84不是一个反素数。

    然后指数不上升也是同理,对于一个较大质因子的指数大于较小质因子的指数的数,在质因子个数不变的情况下,我们完全可以找到一个数,让它的小因子的指数较大而大因子的指数较小,从而使它的数值小于刚才的数,证得刚才的数并不是反素数。再举个例子:对于(540=2^2 imes 3^3 imes 5),存在(360=2^3 imes 3^2 imes 5),360小于540,但约数个数相等,说明540不是反素数。
    蓝书上的对于该部分的说明可能不是很好理解,我觉得以上的解释可能帮助大家理解一下。

    综上所述,思路就是用DFS枚举确定前10个质因子各自的指数(c_i)(c_i in [0,30])),并且满足指数单调递减、总乘积不超过(N),同时记录质因子个数。每找到一个符合条件的数就按照引理1的思路更新答案即可。

    写完了...下面上代码...

    #include <bits/stdc++.h>
    #define int long long
    using namespace std;
    const int maxn = 1e5 + 50;
    const int INF = 0x3f3f3f3f;
    int prime[15]={0,2,3,5,7,11,13,17,19,23,29,31};
    inline int read(){
    	int x = 0, w = 1;
    	char ch;
    	while(ch < '0' || ch > '9'){if(ch == '-')w = -1; ch = getchar();}
    	while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
    	return x * w;
    }
    int n, mx, ans, s[maxn];
    void Dfs(int x, int sum, int maxx){
    	//x表示当前枚举到第几个质因子 
    	//sum表示存在几个约数
    	//maxx为当前的数值 
    	if(x > 10) return;
    	if(sum > mx || (sum == mx && maxx < ans)){
    		mx = sum;
    		ans = maxx;
    	}
    	s[x] = 0;//s[x]记录x位质因子的指数 
    	while(maxx * prime[x] <= n && s[x] < s[x - 1]){
    		s[x]++;
    		maxx = maxx * prime[x];
    		int w = sum * (s[x] + 1);
    		Dfs(x + 1, w, maxx);
    	}
    }
    signed main(){
    	n = read();
    	s[0] = 1000000;
    	Dfs(1, 1, 1);
    	printf("%lld
    ", ans);
    	return 0;
    }
    
  • 相关阅读:
    我的第二个思维导图,用来介绍框架
    如何减少基于DataSet框架的代码输入量(一)
    近日
    关于客户端如何获取服务器时间的方法
    匹配用逗号分隔的数字(防sql注入检查)
    十六进制字符串转整形
    sql获取自增行号
    body不出现滚动条方法
    vs2010 无法调试 无法进入断点 断点无效
    Textarea 高度自适应 根据内容自适应高度
  • 原文地址:https://www.cnblogs.com/Zfio/p/13418925.html
Copyright © 2011-2022 走看看