基础数论应用篇

  • 子集和
    • 题目描述
  • 筛质数
    • 筛质数模板
      • 欧拉筛
      • 线性筛
    • 哥德巴赫猜想
    • 夏洛克和他的女朋友
    • 二次筛法
  • 分解质因数
    • 试除法分解质因数
    • 分解阶乘质因子
  • 快速幂
    • 模板
      • 快速幂
    • 快速乘法
    • 序列的第k个数(模板练习)
    • 组合数 + 快速幂
  • 约数个数
    • 定理
    • 轻拍牛头(约数个数转为倍数思路)
    • 樱花(约数个数+公式推导)
    • 反素数(约数个数+数学知识)

子集和

题目描述

题目描述给你一个元素个数不超过30的整数集合,请你计算该集合所有子集的元素之和。例如集合{1,4},则该集合的子集共四个{{空集}、{1}、{4}、{1、4}},则子集的元素之和为1+4+1+4=10。输入
第一行一个正整数n(1<=n<=30)
第二行n个大小在[1,1000000]范围内的正整数输出
输出给定集合所有子集的元素之和 样例输入
2
1 4 样例输出
10

题目思路及代码
直接用结论元素之和乘以2的n-1次方。下面我们来讲讲为什么有这个结论:
我们依次考虑在每一个数在子集中出现的个数:

筛质数

筛质数模板

欧拉筛

void init(){is_prime[0] = is_prime[1] = 1;for(int i = 2 ; i <= N ; ++i)if(!is_prime[i]){p[cnt++] = i;for(int j = i; j <= N/i ; j ++)is_prime[j*i] = 1;}
}

线性筛

这里我要讲一下线性筛的思路,因为在线性筛的时候有一些特殊的妙用。
即使在优化后(从x²开始), Eratosthenes 筛法仍然会重复标记合数。例如12既会被2又会被3标记,在标记2的倍数时,12=62,在标记3的倍数时,12=43。其根本原因是我们没有确定出唯一的产生12的方式。
线性筛法通过“从大到小累积质因子”的方式标记每个合数,即让12只有322一种产生方式。**设数组v记录每个数的最小质因子,**我们按照以下步骤维护v。1.依次考虑2~N之间的每一个数i。
2.若v[i]=i,说明i是质数,把它保存下来。
3.扫描不大于v[i]的每个质数p,令v[ip]=p。也就是在i的基础上累积一个质因子p。因为p≤v[i],所以p就是合数ip的最小质因子。

用处

  • 筛素数
  • 求每一个数的最小质因子

代码:

void pries(){is_prime[0] = is_prime[1] = 1;memset(v , 0 ,sizeof v); // 最小质因子for(int i = 2 ; i <= N ; ++i){if(!is_prime[i]){v[i] = i ;p[++cnt] = i;}//给当前的数i乘上一个质因子for(int j = 1 ; j <= cnt ; ++j){//如果i有比p[j]更小的质因子,或者超过N的范围,停止循环if(p[j] > v[i] || p[j] > N/i)break;// p[j] 是合数 i * p[j] 的最小质因子v[i*p[j]] = p[j];}}
}

哥德巴赫猜想

题目转送门

解题思路:
本题我们在进行素数筛之后,由到1e6 的质数大约有1e5个,而本题是多组测试数据。因此对于每一个n,我们不能从头或者开始找,而本题说了找的是差值最大的,因此我们可以前通过二分找到在质数数组中第一个大于n的位置开始往后找。

同时虽然哥德巴赫猜想还没有被证明出来,但目前还没有反例,因此答案肯定是存在的。
代码:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1e6 + 10;
int n;
bool is_prime[N];
int cnt ,p[N] ,v[N];void init(){is_prime[0] = is_prime[1] = 1;for(int i = 2 ; i <= N ; ++i)if(!is_prime[i]){p[cnt++] = i;for(int j = i; j <= N/i ; j ++)is_prime[j*i] = 1;}
}int main(){init();while(cin>>n,n)int pos = lower_bound(p , p + cnt , n) - p;for(int i = pos; i ; --i){if(p[i] < n && !is_prime[n - p[i]]){cout<<n << " = " << n - p[i] <<" + "<< p[i]<<endl;pos = i;break;}}return 0;
}

夏洛克和他的女朋友

题目转送门

解题思路:
这题很巧妙,同时我们注意条件使得一件珠宝的价格是另一件珠宝的价格的质因子时,也就是说不相同的两个另一个一定会质数。那么如果我们将质数都染成一种颜色,那么其余的合数都染成另外一种颜色即可。也就是最多需要两个。这是因为质数之间肯定是不符合条件的,在合数中,因为条件说了与其不相同的一定是质数,因此也可以。

代码:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1e5 + 10;
int n ;
int is_prime[N] ,p[N];
int ans[N];void init(int n){for(int i = 2 ; i <= n ; ++i)if(!is_prime[i])for(int j = i ; j <= n / i ; ++j) is_prime[j*i] = 1;
}int main(){cin>>n;init(n+1);bool flag;for(int i = 2 ; i <= n+1 ; ++i)if(!is_prime[i]) ans[i] = 1;else ans[i] = 2 ,flag = 1;if(flag )cout<<2<<endl;else cout<<1<<endl;for(int i = 2 ; i <= n + 1; ++i)cout<<ans[i]<<' ';return 0;
}

二次筛法

题目转送门

解题思路:

出处

因为题目说明左右两端之间的数据不多与1e6,同时为了避免内存爆炸。因此我们在进行二次筛的时候是将值向左偏移L个单位,那么这样最多只有1e6。

代码:

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long LL;
const int N = 1e7+10;
int L,U ;
int l ,r;
bool st[N];
int cnt , prime[N];void init(int n){memset(st , 0 ,sizeof st);cnt = 0;for(int i = 2 ; i <= n ; ++i)if(!st[i]){prime[++cnt] = i;for(int j = i ; j <= n/i ; ++j)st[i*j] = 1;}
}int main(){while(cin>>L>>U){init(50010);memset(st , 0 , sizeof st);for(int i = 1 ; i <= cnt ; ++i){LL p = prime[i];for(LL j = max(2*p , (L + p - 1)/p * p) ; j <= U ; j += p)st[j-L] = true; }cnt = 0;for(int i = 0 ; i <= U - L ; ++i)if(!st[i] && i + L >= 2)prime[cnt++] = i + L;if(cnt < 2) puts("There are no adjacent primes.");else{int minp = 0 , maxp = 0;for(int i = 0 ; i + 1 < cnt ; ++i){int d = prime[i+1] - prime[i];if(d < prime[minp+1] - prime[minp]) minp = i;if(d > prime[maxp+1] - prime[maxp]) maxp = i;}printf("%d,%d are closest, %d,%d are most distant.\n",prime[minp] ,prime[minp+1] ,prime[maxp] ,prime[maxp+1]);}
}return 0;
}

分解质因数

试除法分解质因数

分解质因数:

枚举1~sqrt(n)范围内的所有质因子p(建一个素数表list),判断 p是否是n的因子(能否整除)

  1. ① 如果是,那么给ans结果数组中增加p,并初始化其个数为0。然后,只要p还是n的因子,就不断n除以p,每次操作令p的个数加1,直到p不是n的因子为止
  2. ② 如果不是,直接跳过
    如果在上面的步骤结束后n仍然>1,说明n有且仅有一个大于sqrt(n)的质因子,这时需要把这个质因子加入ans结果数组,令其个数为1
    至此,ans数组中存放的就是质因子分解的结果。

代码:’

void get(int n ){for(int i = 2 ; i <= sqrt(n) ; ++i){if(n % i ){ans[++cnt] = i , c[cnt] = 0;while(n % i ) n /= i , c[cnt]++;}if( n > 1) ans[++cnt] = n , c[cnt]++;
}

分解阶乘质因子

若把1~N每个数分别分解质因数,再把结果合并,时间复杂度过高,为 O(N), 显然,N!的每个质因子都不会超过N,我们可以先筛选出1~N的每个质数p,然后考虑阶乘N!中一共包含多少个质因子p。

N!中质因子p的个数就等于1−N1-N1−N每个数包含质因子p的个数之和。在1~N中,p的倍数,即至少包含1个质因子p的显然有 [N/p][N/p][N/p] 个。而p²p²p²的倍数,即至少包含2个质因子p的有 [N/p2][N/p^2][N/p2]个。不过其中的1个质因子已经在[N/p]里统计过,所以只需要再统计第2个质因子,即累加上 [N/p2][N/p^2][N/p2], 而不是 。2∗[N/p2][N/p^2][N/p2]。 综上所述,N!中质因子p的个数为:
∣N/ρ∣+∣N/ρ2∣+∣N/ρ3∣+⋯+∣N/ρlog⁡pN∣∣N/ρ∣+∣N/ρ2∣+∣N/ρ3∣+⋯+∣N/ρ^{\log{p}{N}}∣∣N/ρ∣+∣N/ρ2∣+∣N/ρ3∣+⋯+∣N/ρlogpN∣
对于每个p,只需要O(1gB)的时间计算上述和式。故对整个N!分解质因数的时间复杂度为O(NlogN)。

在这里我们可以利用除法的思维来求解

这里随带讲下

  1. 为什么在1~N中,p的倍数,即至少包含1个质因子p的显然有 [N/p][N/p][N/p] 个:除法的本质 。那么我们以 p = 2 的时候为例 ,我们 N / 2 下取整 ,就是得到最接近N的一个偶数(可以是自己,如果N是偶素的话),对其平均分,每一份有两个的份数。那为什么这个份数是1`N中能被2整数的个数呢,假设份数为p , 那么我们取出一个得到2,取出2个得到4,… ,那么p份我们就可以通过取出1p份来得出1N中能被2整除的所有数。

代码:

// n! 中质因子p 个个数
int get(int n , int p){int res = 0;for(LL i = p ; i <= n ; i *= p)res += n/i;return res;
}

快速幂

模板

快速幂

至于快速幂的思路大家应该很熟悉了。这里就细讲一下。为什么这样实现。b & 1得到最低位的1 , b >>= 1可以去掉最低位的1. 我们知道快速幂是从小开始迭代起来的(底数跌打)。当系数为1的时候表示我们需要乘当前迭代到的值。至于系数是0的时候为1乘1没有变化因此不用管。 同时还有一个重要原因是取余对于乘法有性质。(a * b) % p =( ( a % p )* ( b % p )) % p

LL power(int a , int b){LL ans = 1 ;while(b){if(b & 1) ans = (LL) ans * a % mod;a = (LL)a * a%mod;b >>= 1;}return ans;
}

快速乘法

我们有
a∗b=a∗ci∗bk−1+a∗cj∗bk−2...+c0∗a∗20a * b = a * c_i * b ^{k-1} + a * c_j * b ^ {k - 2} ... + c_0 * a * 2^0a∗b=a∗ci​∗bk−1+a∗cj​∗bk−2...+c0​∗a∗20由于加法对于取余也有与乘法类似的性质。后一项与前一项的关系为a∗2i=a∗(2i−1)∗2a * 2 ^i = a * ( 2^{i-1}) * 2a∗2i=a∗(2i−1)∗2

a * b
LL mul(int a , int b){LL ans = 0;while(b){if(b & 1) ans = (LL)ans + a %mod;a = (LL) a * 2 % mod;b >>= 1;}return ans;
}

序列的第k个数(模板练习)

  1. 对于等差数列第k项有 :an=a1+(k−1)∗da_n = a_1 + (k-1)*dan​=a1​+(k−1)∗d
  2. 对于等比数列第k项有 :an=a1∗qk−1a_n = a_1 * q^{k-1}an​=a1​∗qk−1

代码:

#include<iostream>
#include<algorithm>
using namespace std;
const int mod = 200907;typedef long long LL;LL power(int a , int b){LL ans = 1 ;while(b){if(b & 1) ans = (LL) ans * a % mod;a = (LL)a * a%mod;b >>= 1;}return ans;
}LL mul(int a , int b){LL ans = 0;while(b){if(b & 1) ans = (LL)ans + a %mod;a = (LL) a * 2 % mod;b >>= 1;}return ans;
}int main(){int T;cin>>T;while(T--){int a ,b , c , k;cin>>a>>b>>c>>k;if(a + c == 2*b)cout<<( a + mul(b - a , k - 1)) % mod<<endl;elsecout<<(LL)a*power(b/a ,k - 1) % mod<<endl;}return 0;
}

组合数 + 快速幂

题目转送门

解题思路:

同时对于结果,对于有一个额外的数乘上快速幂的结果我们需要对这个结果再来一次取模。对于相减可能得负数的情况,我们可以通过加mod 再取模解决;

代码:

#include<iostream>
#include<algorithm>
using namespace std;
const int mod = 100003 ;typedef long long LL;
LL n , m ;LL power(LL a , LL b){LL ans = 1 % mod;while(b){if(b & 1) ans = (LL) ans * a % mod;a = (LL) a * a % mod;b >>= 1;}return ans ;
}int main(){cin>>m>>n;cout<<  ( power(m , n) - ((LL) m * power(m-1,n-1)) % mod + mod)  % mod<<endl;return 0;
}

约数个数

定理


这就是一个排列组合问题。我们知道p和c后,那么pnp^npn一共能组成p0..pnp^0..p^np0..pn一共(n+1)种数。其中的每一个数就能和其余p和c组成的 (m+1)中数组合成一个新约数。因此由乘法原理,方案数为上面式子。

轻拍牛头(约数个数转为倍数思路)

题目转送门

解题思路:
这题问的是一个数,其余的数是其约数的个数(不包括自己)
在这里由于数据过大,因此我们不能通过求约数的方法来求。对于某一个值i ,我们令其倍数加上其出现的次数就可以了。这个看似是n^2复杂度,其实与求1-N的约数个数一样,只是NlongN。

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N = 1E6 + 10;int n;
int cnt[N] , a[N] , s[N];int main(){cin>>n;for(int i = 1 ; i <= n ; ++i)scanf("%d",&a[i]) , cnt[a[i]]++;for(int i = 1 ; i <= N ; ++i)for(int j = 1 ; j <= N / i ; ++j)s[j*i] += cnt[i];for(int i = 1 ; i <= n ; ++i)printf("%d\n",s[a[i]] - 1);return 0;
}

樱花(约数个数+公式推导)

题目转送门

解题思路:
针对有两个变量得式子,我们可以试着将其中一个变量写成只含义另一个变量的形式。那么题目就转变成了,后边式子有多少种选法使得,y满足条件。

对于这个题目,化简有
y=x(n!)x−n!y = \frac{x(n!)}{x - n!} \quad y=x−n!x(n!)​

对x进行变形,右分母的形式我们有
y=(x−n!+n!)(n!)x−n!=n!+n!2x−n!y = \frac{(x-n!+n!)(n!)}{x - n!} \quad = n! + \frac{n!^2}{x - n!} \quad y=x−n!(x−n!+n!)(n!)​=n!+x−n!n!2​
有y为正整数,n!也一定是正整数。那么这题就转变为了x有多少种选法使得
n!2x−n!\frac{n!^2}{x - n!} \quad x−n!n!2​为正整数。能被n!2n!^2n!2整除的就是其约数,因为x取值为正整数集,因此分母的约数一定能表示出来。那么这题就变成了求n!2n!^2n!2中约数的个数。

我们对定理出发有

代码:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1E6 + 10 , mod = 1e9 + 7;int n ;
int primes[N] , cnt;
bool st[N];void init(int n ){for(int i = 2; i <= n ; ++i)if(!st[i]){primes[cnt++] = i;for(int j = 1 ; j <= n/i ; ++j) st[i*j] = 1;}
}int get(int n , int p){int ans = 0;while(n){ans += n/p;n /= p;}return ans * 2;
}int main(){cin>>n;int ans = 1;init(n);for(int i = 0 ; i < cnt && primes[i] <= n ; ++i)ans = (long long )ans * ( 1 + get(n , primes[i])) % mod;cout<<ans<<endl;return 0;
}

反素数(约数个数+数学知识)

题目转送门

解题思路:

  1. 引理一:1-N中最大的反质数是1-N中约数最大的数中值最小的一个
  2. 引理二: 1~N中任何数的不同质因子都不会超过10个。因为11个的话2*3…29 * 31 > 2 * 10^9 大于题目所给范围。
  3. 引理三: x 为反质数分必要条件是:x 可以分解为2c1∗3c2....∗39c10,c1>=c2>=...>=c102^{c_1}*3^{c_2}....*39^{c_{10}},c_1>= c_2 >= ...>=c_{10}2c1​∗3c2​....∗39c10​,c1​>=c2​>=...>=c10​

综上所述,我们可以使用深度优先搜索(DFS),尝试依次确定前10个质数的指数,并满足指数单调递减、总乘积不超过N,同时记录约数的个数。
在这两个限制条件下,搜索量实际上非常小。每当搜索出一个满足条件的整数时,我们就按照引理1的结论更新答案,最终得到约数个数最多的数中最小的一个。

代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int N = 1E5;
typedef unsigned long long LL;
int n;
LL maxn = 0, ans ;
int primes[11] = {2,3,5,7,11,13,17,19,23,29};void dfs(int s ,LL sum , int p , LL cnt){if(sum > n)return ;if(p == 10 && sum <= n){if(cnt == maxn)ans = min(ans , sum);if(cnt > maxn) ans = sum;maxn = max(maxn ,cnt);return ;}if(s == 0 ) return ;for(int i = s ; i >= 0 ; --i)dfs(s,sum * (LL)pow(primes[p],i) , p+1 , cnt * (i+1));}int main(){cin>>n;ans = N;dfs(9,1,0 , 1);cout<<ans<<endl;return 0;
}

算法之基础数论应用篇(一)相关推荐

  1. 欧几里得定理 java,每个程序员都应该知道的基础数论

    原标题:每个程序员都应该知道的基础数论 这篇文章讨论了数论中每个程序员都应该知道的几个重要概念.本文的内容既不是对数论的入门介绍,也不是针对数论中任何特定算法的讨论,而只是做为数论的一篇参考. 0. ...

  2. 菜鸟学算法一基础知识篇

    菜鸟学算法<一>知识准备篇 刚刚上任,急着给兄弟们一点见面礼,嘿嘿 前言:论坛上有关算法分析的文章不少,也不少精品 但对于刚学CARACK来说,只是叹为观止 原因如下: 1.论坛高手如云, ...

  3. 算法之数论应用篇(二)

    算法之数论应用篇二 最大公约数 线性筛 Hankson的趣味题 欧拉函数 前言 可见的点(数学知识+欧拉函数) 最大公约数(可见的点扩展) 同余 取模的性质 定义 基本性质 运算规则 重要定理 重要定 ...

  4. RSA算法原理——(2)RSA简介及基础数论知识

    上期为大家介绍了目前常见加密算法,相信阅读过的同学们对目前的加密算法也算是有了一个大概的了解.如果你对这些解密算法概念及特点还不是很清晰的话,昌昌非常推荐大家可以看看HTTPS的加密通信原理,因为HT ...

  5. 图解排序算法(基础篇)

    文章目录 一.冒泡排序 1.1 冒泡排序的第一种写法 1.2 冒泡排序的第二种写法 1.3 冒泡排序的第三种写法 二.选择排序 2.1 二元选择排序 三.插入排序 排序算法是一类非常经典的算法,说来简 ...

  6. 【机器学习】机器学习算法优缺点对比(汇总篇)

    作者 | 杜博亚 来源 | 阿泽的学习笔记 「本文的目的,是务实.简洁地盘点一番当前机器学习算法」.文中内容结合了个人在查阅资料过程中收集到的前人总结,同时添加了部分自身总结,在这里,依据实际使用中的 ...

  7. 清华校友斩获ACM博士论文奖!相关研究为自动驾驶新算法奠定基础

    点击上方"视学算法",选择加"星标"或"置顶" 重磅干货,第一时间送达 明敏 萧箫 发自 凹非寺 量子位 报道 | 公众号 QbitAI 今 ...

  8. 「机器学习」机器学习算法优缺点对比(汇总篇)

    作者 | 杜博亚 来源 | 阿泽的学习笔记 「本文的目的,是务实.简洁地盘点一番当前机器学习算法」.文中内容结合了个人在查阅资料过程中收集到的前人总结,同时添加了部分自身总结,在这里,依据实际使用中的 ...

  9. 算法刷题-数论-试除法求约数、约数个数、约数之和、最大公约数(辗转相除法)

    文章目录 acwing869. 试除法求约数 acwing870. 约数个数 acwing871. 约数之和 acwing872. 最大公约数 acwing869. 试除法求约数 acwing869. ...

最新文章

  1. mysql错误用法insert into where
  2. 【Tools】Visual Studio 2017下载和安装
  3. 增强DropDownList和ListBox控件:保持客户端脚本添加的options
  4. 部署superset_ubuntu16下部署apache superset趟坑指南(内有福利)
  5. linux中模拟延时与丢包的实现
  6. POJ-Prime Gap 素数筛选+二分查找
  7. Python3高级 之 协程
  8. 计算机三级嵌入式系统考试之矩阵键盘
  9. SpringBoot 注入的@service为空,运行时报空指针
  10. POJ 1981 Circle and Points 单位圆覆盖
  11. 中美两本有影响力数理统计学教材的对比及其启示(龚凤乾)
  12. C++ 负数在内存中存储
  13. 微信浪漫告白小程序java_微信表白小程序有哪些?微信小程序520表白神器推荐...
  14. python下载安装图文教程-Pycharm下载安装图文教程
  15. Hadoop,Spark错误:Could not locate executable null\bin\winutils.exe in the Hadoop binaries.
  16. Typora+PciGo-Core+SMMS自动上传图片
  17. java jdk7.0安装包_jdk7下载|java jdk 1.7下载 安装包 - 跑跑车手游网
  18. 基于javaweb的图书管理系统(java+jsp+layui+bootstrap+servlet+mysql)
  19. 如何解决@RequestParam无法接收vue+axios传递json数据
  20. python运维系统开发_Python系统运维开发实战

热门文章

  1. linux c语言中如何通过进程名获取进程PID(awk命令行指令)popen、pclose
  2. 计算机中flow和stream还有torrent有什么区别?(五元组、microflow、traffic flow)
  3. C++内联 inline的用法
  4. python Matplotlib.pyplot 如何绘制三维折线图, 散点图, 线框图, 表面图, 柱状图, 箭头图, 2D转3D图, 文本图, 3D拼图, 网状图, 直方图, 角面片图, 条状图?
  5. Nginx教程系列五:实现负载均衡配置
  6. Java进阶:ArrayList线程安全问题和解决方案
  7. java jdbc脚本_关于java:使用MySQL和JDBC运行.sql脚本
  8. springmvc的配置
  9. 「后端小伙伴来学前端了」Vue-Router 路由各种跳转、传参、小知识
  10. SpringBoot集成MybatisPlus 涵盖了目前流行的知识点!!!即用即cv即可!!!学过的同学,也可以存储作为工具!!