zoukankan      html  css  js  c++  java
  • 洛谷 P4397 [JLOI2014]聪明的燕姿 / TOPOI 测验1315, 问题E: 1935: 聪明的燕姿 题解

    题目链接 : 

    1. 洛谷

    2.topoi

    大致题意:输入一个数s,找出所有约数和为s的数

    关于一个数的约数和求法

    一个>1的整数可以被分解为多个 质数 的乘方,设数 s = p1k1 * p2k2 * p3k3  *......*pnkn

    根据 组合 的思想  s的约数和 = (p10 +p11+p12+......+p1k1)*p20 +p21+p22+......+p2k2)*........*pn0 +pn1+pn2+......+pnkn);

    数据很大,有多组测试数据,首先想到预处理

    预处理 2 * 10^9内的所有质数?(神仙也没法阻止你TLE

    事实上只要处理√2*10^9内的质数行了Why? 注:√2*10^9 ≈ 50000

    设输入的数为s,有一个质数 k>√s, 在分解s的过程中,k最多只能取1个

    所以在找答案的过程中,只要判断当前分解的数 a = k + 1(k+k0=k+1)就可以加入答案

    但在实际搜索的过程中,假设当前在枚举第 x 个质数,分解的数为 a,当a - 1 > t[x] 且 (a - 1)是质数时,就可以加入答案

    因为如果(a-1)是质数,之后枚举的所有质数 p,一定满足 p * p <= a (前面已经提到只枚举 < √a 的质数),所以 p ≠ a-1,不会造成答案重复

    预处理质数 :

    范围虽然开了根号,但用普通筛选肯定不行,(估计TOPOI又要炸了

    这里要用到线性筛来预处理

    线性筛主体思想:让每一个数只筛选出部分合数,其余的让别的数来筛,时间复杂度 O(n)

    实现:

    for (register int i = 2; i <= maxn; i++) {
            if (!ok[i])  t[++num] = i;
            for (register int j = 1; j <= num; j++) {
                if (t[j] * i > maxn) break;
                ok[t[j] * i] = 1;
                if (i % t[j] == 0) break;
            }
        }  //线性筛 预处理 maxn 内的质数
    

      

    处理出质数后,就可以开始暴力枚举

    搜索的时候 从n除到1从1乘到n都可以 (因为运算符不同, 从n除到1貌似更快一些

    这里附上 2 种做法的代码(差不多)

    (代码跟 洛谷里的题解 差不多,毕竟我也是看了题解

    细节看一下代码中的注释吧 (代码巨丑

    代码1:

    #include <bits/stdc++.h>
    using namespace std;
    #define maxn 50000
    int n, num, ans[maxn * 2], t[maxn + 10], tot;  //ans存答案,t存质数,tot记录答案数
    bool ok[maxn + 10];        //ok表示是否是质数
    bool check (int x) {
    	if (x <= maxn) return !ok[x];   //优化 :若这个数已经预处理过,就不用重复做了 
    	for (register int i = 1; t[i] * t[i] <= x; i++) if (x % t[i] == 0) return false;
    	//这里判断质数只要用前面 质数 来判断就行了,节省时间 
    	return true;
    }
    void dfs (int x, int sum, int now) {
    	if (sum == 1) {ans[++tot] = now;  return;}  //当分解到 1 时说明方案成立,加入答案 
    	if (sum > t[x] + 1  and check (sum - 1)) ans[++tot] = now * (sum - 1);  //特判是否满足上述情况 
    	for (register int k = x + 1; t[k] * t[k] <= sum; k++)    //外重枚举质数 
    		for (register int ss = 1 + t[k], sn = t[k]; ss <= sum; sn *= t[k], ss += sn) {//内重枚举个数 
    			if (sum % ss != 0) continue;   //剪枝 :如果除不尽就别做了 
    			dfs (k, sum / ss, now * sn);
    		}
    }
    int main() {
    	for (register int i = 2; i <= maxn; i++) {
    		if (!ok[i])  t[++num] = i;
    		for (register int j = 1; j <= num; j++) {
    			if (t[j] * i > maxn) break;
    			ok[t[j] * i] = 1;
    			if (i % t[j] == 0) break;
    		}
    	}  //线性筛 预处理 maxn 内的质数
    	while (~scanf ("%d", &n)) {   //~scanf实现多组数据读入
    		tot = 0;
    		dfs (0, n, 1);
    		sort (ans + 1, ans + tot + 1);
    		printf ("%d
    ", tot);
    		for (register int i = 1; i <= tot; i++) printf ("%d ", ans[i]);
    		if (tot) puts ("");
    	}
    	return 0;
    }

     

    代码2:

    #include <bits/stdc++.h>
    using namespace std;
    #define maxn 50000
    int n, num, ans[maxn * 2], t[maxn + 10], tot;  //ans存答案,t存质数,tot记录答案数
    bool ok[maxn + 10]; //ok表示是否是质数
    bool check (int x) {
    	if (x <= maxn) return !ok[x];   //优化 :若这个数已经预处理过,就不用重复做了 
    	for (register int i = 1; t[i] * t[i] <= x; i++) if (x % t[i] == 0) return false;
    	//这里判断质数只要用前面 质数 来判断就行了,节省时间 
    	return true;
    }
    void dfs (int x, int sum, int now) {
    	if (sum == n) {ans[++tot] = now;  return;}   //当乘积到 n 时说明方案成立,加入答案 
    	if (n / sum > t[x] + 1  and check (n / sum - 1)) ans[++tot] = now * (n / sum - 1);  //特判是否满足上述情况 
    	for (register int k = x + 1; t[k] * t[k] <= n / sum; k++)   //外重枚举质数 
    		for (register int ss = 1 + t[k], sn = t[k]; ss <= n / sum; sn *= t[k], ss += sn) { //内重枚举个数 
    			if ((n / sum) % ss != 0) continue; //剪枝 :如果除不尽就别做了 
    			dfs (k, sum * ss, now * sn);
    		}
    }
    int main() {
    	for (register int i = 2; i <= maxn; i++) {
    		if (!ok[i])  t[++num] = i;
    		for (register int j = 1; j <= num; j++) {
    			if (t[j] * i > maxn) break;
    			ok[t[j] * i] = 1;
    			if (i % t[j] == 0) break;
    		}
    	}  //线性筛 预处理 maxn 内的质数
    	while (~scanf ("%d", &n)) {   //~scanf实现多组数据读入
    		tot = 0;
    		dfs (0, 1, 1);
    		sort (ans + 1, ans + tot + 1);
    		printf ("%d
    ", tot);
    		for (register int i = 1; i <= tot; i++) printf ("%d ", ans[i]);
    		if (tot) puts ("");
    	}
    	return 0;
    }
    

      

    点一下左边的推荐吧!谢谢~~~

  • 相关阅读:
    日活跃用户统计函数
    统计学习方法(五)——决策树
    统计学习方法(四)——朴素贝叶斯法
    统计学习方法(三)——K近邻法
    统计学习方法(二)——感知机
    Hive UDAF开发之同时计算最大值与最小值
    hive UDAF开发入门和运行过程详解(转)
    Linux操作系统启动流程
    Linux目录的作用
    Linux分区
  • 原文地址:https://www.cnblogs.com/whx666/p/10400951.html
Copyright © 2011-2022 走看看