【笔记整理】简单数论
简单数论
素数
试除法
复杂度:\(O(\sum_{i=1}^n\sqrt i) = O(n\sqrt n)\)
void brute() {
for (int i = 2; i <= n; ++i) {
isp[i] = 1;
for (int j = 2; j*j <= n && j < i; ++j) {
if (i%j == 0) isp[i] = 0;
}
}
}
埃氏筛
memset(is_prime, 1, sizeof(is_prime));
- \(\forall p\in\mathbb P\),
is_prime[p*x]=0;
(\(x\ge p\)),primes.push_back(i);
复杂度:\(O(\sum_{p\in\mathbb P, p \le n}\frac{n}{p})\),根据Mertens' 2nd theorem:
所以为$O(n\log\log n )$
void eratos() {
memset(isp, 1, sizeof(isp));
for (int i = 2; i <= n; ++i) {
if (isp[i]) {
for (int j = 2; j <= n/i; ++j) isp[i*j] = 0;
p.push_back(i);
}
}
}
POJ2689:Prime Distance
Your program is given 2 numbers: \(L\) and \(U\) (\(1\le L< U\le 2,147,483,647\)), and you are to find the two adjacent primes \(C_1\) and \(C_2\) (\(L\le C_1< C_2\le U\)) that are closest (i.e. \(C_2-C_1\) is the minimum).
For each \(L\) and \(U\), the output will either be the statement that there are no adjacent primes (because there are less than two primes between the two given numbers) or a line giving the two pairs of adjacent primes.
解
我们只需要知道\([U,L]\)中每个数是不是素数即可:
- 筛出所有\([2,\sqrt{U}]\)中的素数
- \(\forall p \in \mathbb P\),\(\forall i \in\left[\left\lfloor\dfrac{L}{p}\right\rfloor,\left\lfloor\dfrac{U}{p}\right\rfloor\right]\),
isp[i*p]=0;
//坑待填
优化整数分解
令 \(i\) 的最小的大于 \(1\) 的factor[i]=i
。
void eratos() {
for (int i = 2; i <= n; ++i) {
if (isp[i]) {
for (int j = 2; j <= n/i; ++j) {
isp[i*j] = 0;
if (fact[i*j] == i*j) fact[i] = i;
}
}
}
}
std::vector<int>& factor(int x) {
std::vector<int> v;
while (x > 1) {
v.push_back(fact[x]);
x /= fact[x];
}
return v;
}
CH3101:阶乘分解
描述:
给定整数 \(1\le N \le 10^6\),试把阶乘 \(N!\) 分解质因数,按照算术基本定理的形式输出分解结果中的 \(p_i\) 和 \(c_i\) 即可。
输入格式:
一个整数N。
输出格式:
N! 分解质因数后的结果,共若干行,每行一对pi, ci,表示含有pi^ci项。按照pi从小到大的顺序输出。
解
-
筛出 \([1, n]\) 中的所有素数。
-
\(\forall p \in \mathbb P\) ,求\(\sum^{\log_pN}_{i = 0}\lfloor\frac{N}{p}\rfloor\)即可。
int n;
bool isp[maxn];
std::vector<int> p;
void eratos() {
memset(isp, 1, sizeof(isp));
for (int i = 2; i <= n; ++i) {
if (isp[i]) {
for (int j = 2; j <= n/i; ++j) isp[i*j] = 0;
p.push_back(i);
}
}
}
int count(int x, int p) {
int res = 0;
while (x) res += (x /= p);
return res;
}
void solve() {
for (int i = 0; i < p.size(); ++i) {
printf("%d %d\n", p[i], count(n, p[i]));
}
}
欧拉筛(线性筛)
埃氏筛中,一个和数被其所有质因子都筛了一遍,浪费大量时间。
我们令一个和数只被其最小的素因数筛一次,就可以做到线性了。
void euler() {
memset(isp, 1, sizeof(isp));
for (int i = 2; i <= n; ++i) {
if (isp[i]) p.push_back(i);
for (int j = 0; j < p.size() && p[j]*i <= n; ++j) {
isp[p[j]*i] = 0;
if (i%p[j] == 0) break;
}
}
}
约数
约数个数
设\(n=\prod_i p_i^{r_i}\),\(n\)的约数个数为\(\prod_i (r_i+1)\),和为\(\prod_i (\sum_{j=0}^{r_i}p_i^j) = \prod_i (\frac{1-p^{r_i+1}}{1-p})\)
BZOJ1053:反素数ant
对于任何正整数\(x\),其约数的个数记作\(g(x)\)。例如\(g(1)=1\)、\(g(6)=4\)。如果某个正整数\(x\)满足:\(g(x)>g(i),\forall\ 0<i<x\),则称x为反质数。例如,整数\(1\),\(2\),\(4\),\(6\)等都是反质数。现在给定一个数\(N\le 2\times10^9\),求不超过\(N\)的最大的反质数。
解
- 引理1:由于\(\prod_{i = 1}^{10}p_i = 6469693230 > N\),不超过\(N\)的数没有超过10个不同的的素因子。
- 引理2:若\(n = \prod p_i^{r_i}, \exist{p_i<p_j, r_i<r_j}\),则\(n\)不为反素数。
- 由于满足以上两个条件的数个数极少(1456个?),dfs爆搜即可。
const long long p[11] = {1,2,3,5,7,11,13,17,19,23,29};
int n, r[11], ans, num;
void dfs(int x, int sum, int prod) {
if (sum>num || (sum==num && prod<ans)) {
ans = prod;
num = sum;
}
r[x] = 0;
while (prod*p[x]<=n && r[x]<r[x-1]) {
++r[x];
prod*=p[x];
dfs(x+1, sum*(r[x]+1), prod);
}
}
int main() {
scanf("%d", &n);
r[0] = 32; //2^31 > 2e9
dfs(1, 1, 1);
printf("%d", ans);
}
最大公约数
\(\gcd(x,y) = \gcd(y,x) = \gcd(x, x\pm y) = \gcd(x, y/d)\ (d\mid y, d\nmid x)\)
欧几里得算法(辗转相除法):
\(\gcd(x, y) = \gcd(x, y \bmod x)\ (x\ne0)\)
int gcd(int x, int y) {
return y?gcd(y, x-y):x;
}
BZOJ1876:SuperGCD
Input:
共两行: 第一行:一个数A。 第二行:一个数B。
\(0 < A , B \le 10^{10000}\)。
Output:
一行,表示A和B的最大公约数。
解
直接高精度除法是\(O(|n|^3)\)的,怎么办?
我们有更相减损术!
\(O(|n|^2)\).
//代码太长就不贴了……
//温馨提示:要压位!
最小公倍数
\(\operatorname{lcm}(a,b)=\frac{|a\cdot b|}{\operatorname{gcd}(a,b)}\)
long long lcm(int x, int y) {
if (x == 0 || y == 0) return 0;
return (long long)x/gcd(x, y)*y;
}