AcWing算法提高课 数学知识
AcWing算法提高课 数学知识
筛质数
哥德巴赫猜想的内容如下:
任意一个大于 44 的偶数都可以拆成两个奇素数之和。
例如:
8=3+58=3+5
20=3+17=7+1320=3+17=7+13
42=5+37=11+31=13+29=19+2342=5+37=11+31=13+29=19+23现在,你的任务是验证所有小于一百万的偶数能否满足哥德巴赫猜想。
输入格式
输入包含多组数据。
每组数据占一行,包含一个偶数 nn。
读入以 00 结束。
输出格式
对于每组数据,输出形如
n = a + b
,其中 a,ba,b 是奇素数。若有多组满足条件的 a,ba,b,输出 b−ab−a 最大的一组。
若无解,输出
Goldbach's conjecture is wrong.
。数据范围
6≤n<1066≤n<106
输入样例:
8 20 42 0
输出样例:
8 = 3 + 5 20 = 3 + 17 42 = 5 + 37
线性筛
#include <bits/stdc++.h> using namespace std; const int N = 1000005; int n; int p[N], cnt; bool st[N]; void get_primes() { st[0] = st[1] = 1; for (int i = 2; i <= N; i ++ ) { if (!st[i]) p[cnt ++ ] = i; for (int j = 0; i * p[j] <= N; j ++ ) { st[i * p[j]] = 1; if (i % p[j] == 0) break; } } } int main() { get_primes(); while (cin >> n, n) { for (int i = 0; i < n; i ++ ) if (!st[i] && !st[n - i]) { printf("%d = %d + %d\n", n, i, n - i); break; } } return 0; }
题目:
夏洛克有了一个新女友(这太不像他了!)。
情人节到了,他想送给女友一些珠宝当做礼物。
他买了 nn 件珠宝,第 ii 件的价值是 i+1i+1,也就是说,珠宝的价值分别为 2,3,…,n+12,3,…,n+1。
华生挑战夏洛克,让他给这些珠宝染色,使得一件珠宝的价格是另一件珠宝的价格的质因子时,两件珠宝的颜色不同。
并且,华生要求他使用的颜色数尽可能少。
请帮助夏洛克完成这个简单的任务。
输入格式
只有一行一个整数 nn,表示珠宝件数。
输出格式
第一行一个整数 kk,表示所使用的颜色数;
第二行 nn 个整数,表示第 11 到第 nn 件珠宝被染成的颜色。
若有多种答案,输出任意一种。
请用 11 到 kk 表示你用到的颜色。
数据范围
1≤n≤1051≤n≤105
输入样例1:
3
输出样例1:
2 1 1 2
输入样例2:
4
输出样例2:
2 2 1 1 2
分析:
我们让素数是一种颜色,合数是另一种颜色
代码:
#include <bits/stdc++.h> using namespace std; const int N = 100005; int n; int p[N], cnt; bool st[N]; void get_prime(int n) { st[0] = 1; st[1] = 1; for (int i = 2; i <= n; i++ ) { if (!st[i]) p[cnt ++ ] = i; for (int j = 0; p[j] * i <= n; j ++ ) { st[i * p[j]] = 1; if (i % p[j] == 0) break; } } } int main() { cin >> n; get_prime(n + 1); if (n > 3) cout << 2 << endl; else cout << 1 << endl; for (int i = 2; i <= n + 1; i ++ ) if (!st[i]) printf("%d ", 1); else printf("%d ", 2); }
题目:
给定两个整数 LL 和 UU,你需要在闭区间 [L,U][L,U] 内找到距离最接近的两个相邻质数 C1C1 和 C2C2(即 C2−C1C2−C1 是最小的),如果存在相同距离的其他相邻质数对,则输出第一对。
同时,你还需要找到距离最远的两个相邻质数 D1D1 和 D2D2(即 D1−D2D1−D2 是最大的),如果存在相同距离的其他相邻质数对,则输出第一对。
输入格式
每行输入两个整数 LL 和 UU,其中 LL 和 UU 的差值不会超过 106106。
输出格式
对于每个 LL 和 UU,输出一个结果,结果占一行。
结果包括距离最近的相邻质数对和距离最远的相邻质数对。(具体格式参照样例)
如果 LL 和 UU 之间不存在质数对,则输出
There are no adjacent primes.
。数据范围
1≤L<U≤231−11≤L<U≤231−1
输入样例:
2 17 14 17
输出样例:
2,3 are closest, 7,11 are most distant. There are no adjacent primes.
分析:
将L到R的合数筛出来,用st[i - L]表示
代码
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1000010; int p[N], cnt; bool st[N]; bool vis[N]; void init() { for (int i = 2; i <= N; i ++ ) { if (!st[i]) p[cnt ++ ] = i; for (int j = 0; p[j] * i <= N; j ++ ) { st[i * p[j]] = 1; if (i % p[j] == 0) break; } } } int w[N]; int main() { init(); // for (int j = 0; j < cnt; j ++ ) cout << p[j] << " "; // cout << endl; int l, r; while (cin >> l >> r) { memset(vis, 0, sizeof vis); for (int i = 0; i < cnt; i ++ ) { ll pp = p[i]; for (ll j = max(2 * pp, (l + pp - 1) / pp * pp); j <= r; j += pp ) vis[j - l] = true; } int tot = 0; for (int i = 0; i <= r - l;i ++ ) if (!vis[i] && i + l >= 2) w[tot ++ ] = i + l; if (tot < 2) puts("There are no adjacent primes."); else { int mi = 0, mx = 0; for (int i = 0; i + 1 < tot; i ++ ) { int d = w[i + 1] - w[i]; if (d < w[mi + 1] - w[mi]) mi = i; if (d > w[mx + 1] - w[mx]) mx = i; } printf("%d,%d are closest, %d,%d are most distant.\n", w[mi], w[mi + 1], w[mx], w[mx + 1]); } // for (int i = l; i <= r; i ++ ) // cout << vis[i - l] << " "; // cout << endl; // int start = l, last = l; // int a = 2e9, b = -2e9; // for (int i = l + 1; i <= r; i ++ ) // { // if (!vis[i - l] && !vis[start - l] && a > i - start + 1) // { // a = i - start + 1; // start = i; // } // if (!vis[i - l] && !vis[last - l] && b < i - last + 1) // { // b = i - last + 1; // last = i; // } // } // // cout << a << endl; // if (a == 2e9) puts("There are no adjacent primes."); // else printf("%d,%d are closest, %d,%d are most distant.\n", start - a + 1, start, last - b + 1, last); } }
分解质因数
题目:
给定整数 N,试把阶乘 N! 分解质因数,按照算术基本定理的形式输出分解结果中的 pi 和 ci 即可。
输入格式
一个整数 N。
输出格式
N! 分解质因数后的结果,共若干行,每行一对 pi,ci,表示含有 pi^ci 项。按照 pi 从小到大的顺序输出。
数据范围
1≤N≤106
输入样例:
5
输出样例:
2 3 3 1 5 1
样例解释
5!=120=23∗3∗5
分析:
求N!中质因子p的个数,那么我们知道在1~N中,有N / p个p的倍数,有N / (P ^ 2)个p^2个质因子。
代码:
#include <bits/stdc++.h> using namespace std; const int N = 1000005; int n; int p[N], cnt; bool st[N]; void get_primes(int n) { for (int i = 2; i <= n; i ++ ) { if (!st[i]) p[cnt ++ ] = i; for (int j = 0; i * p[j] <= n; j ++ ) { st[i * p[j]] = true; if (i % p[j] == 0) break; } } } int main() { cin >> n; get_primes(n); for (int i = 0; i < cnt; i ++ ) { int s = 0, pp = p[i]; for (int j = n; j; j /= pp) { s += j / pp; } printf("%d %d\n", pp, s); } }
快速幂
题目:
BSNY 在学等差数列和等比数列,当已知前三项时,就可以知道是等差数列还是等比数列。
现在给你 整数 序列的前三项,这个序列要么是等差序列,要么是等比序列,你能求出第 k 项的值吗。
如果第 k 项的值太大,对其取模 200907。
输入格式
第一行一个整数 T,表示有 T 组测试数据;
对于每组测试数据,输入前三项 a,b,c,然后输入 k。
输出格式
对于每组数据,输出第 k 项取模 200907 的值。
数据范围
1≤T≤100,
1≤a≤b≤c≤109,
1≤k≤109输入样例:
2 1 2 3 5 1 2 4 5
输出样例:
5 16
分析:
等差数列:a + c = 2 * b
等比数列:a * c = b * b
代码:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int mod = 200907; ll qmi(ll a, ll b) { ll ans = 1; while (b) { if (b & 1) ans = ans * a % mod; b >>= 1; a = a * a % mod; } return ans; } ll a, b, c, k; int main() { int T; cin >> T; while (T -- ) { cin >> a >> b >> c >> k; if (a + c == b << 1) { ll d = b - a; cout << (a + (k - 1) * d) % mod << endl; } else { ll q = b / a; ll ans = a * qmi(q, k - 1) % mod; cout << ans << endl; } } }
题目:
监狱有连续编号为 1 到 n 的 n 个房间,每个房间关押一个犯人。
有 m 种宗教,每个犯人可能信仰其中一种。
如果相邻房间的犯人信仰的宗教相同,就可能发生越狱。
求有多少种状态可能发生越狱。
输入格式
共一行,包含两个整数 m 和 n。
输出格式
可能越狱的状态数,对 100003 取余。
数据范围
1≤m≤108,
1≤n≤1012输入样例:
2 3
输出样例:
6
样例解释
所有可能的 6 种状态为:(000)(001)(011)(100)(110)(111)。
分析:
容斥,将全集减去合法集
代码:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int mod = 100003; ll qmi(ll a, ll b) { ll ans = 1; while (b) { if (b & 1) ans = ans * a % mod; b >>= 1; a = a * a % mod; } return ans; } ll n, m; int main() { cin >> m >> n; cout << (qmi(m, n) % mod - m * qmi(m - 1, n - 1) % mod + mod) % mod << endl; }
约数个数
题目:
今天是贝茜的生日,为了庆祝自己的生日,贝茜邀你来玩一个游戏.
贝茜让 N 头奶牛(编号 1 到 N)坐成一个圈。
除了 1 号与 N 号奶牛外,i 号奶牛与 i−1 号和 i+1 号奶牛相邻,N 号奶牛与 1 号奶牛相邻。
农夫约翰用很多纸条装满了一个桶,每一张纸条中包含一个 1 到 1000000 之间的数字。
接着每一头奶牛 i 从桶中取出一张纸条,纸条上的数字用 Ai 表示。
所有奶牛都选取完毕后,每头奶牛轮流走上一圈,当走到一头奶牛身旁时,如果自己手中的数字能够被该奶牛手中的数字整除,则拍打该牛的头。
牛们希望你帮助他们确定,每一头奶牛需要拍打的牛的数量。
即共有 N 个整数 A1,A2,…,AN,对于每一个数 Ai,求其他的数中有多少个是它的约数。
输入格式
第一行包含整数 N。
接下来 N 行,每行包含一个整数 Ai。
输出格式
共 N 行,第 i 行的数字为第 i 头牛需要拍打的牛的数量。
数据范围
1≤N≤105,
1≤Ai≤106输入样例:
5 2 1 2 3 4
输出样例:
2 0 2 1 3
分析:
计算每个数在n个数字中出现了多少个他的约数。
那么,我们将每个数字用cnt存起来,将所有合数在n中出现的约数都算出来,然后查表
代码:
#include <bits/stdc++.h> using namespace std; const int N = 1000005; int n; int a[N], cnt[N]; int ans[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 = i; j < N; j += i ) ans[j] += cnt[i]; for (int i = 1; i <= n; i ++ ) printf("%d\n", ans[a[i]] - 1);//多加了一个它自己 return 0; }
题目:
给定一个整数 n,求有多少正整数数对 (x,y) 满足 1/x+1/y=1/n!。
输入格式
一个整数 n。
输出格式
一个整数,表示满足条件的数对数量。
答案对 10^9+7 取模。
数据范围
1≤n≤10^6
输入样例:
2
输出样例:
3
样例解释
共有三个数对 (x,y) 满足条件,分别是 (3,6),(4,4),(6,3)。
分析:
这个题,重点就是对数学公式的推导,需要手推出来,才能看出来性质。
推导省略了,详看AcWing
代码:
#include <bits/stdc++.h> using namespace std; const int N = 1000005, mod = 1e9 + 7; int n; int primes[N], cnt; bool st[N]; void get_primes(int n) { for (int i = 2; i <= n; i ++ ) { if (!st[i]) primes[cnt ++ ] = i; for (int j = 0; primes[j] * i <= n; j ++ ) { st[i * primes[j]] = 1; if (i % primes[j] == 0) break; } } } int main() { cin >> n; get_primes(n); long long ans = 1; for (int i = 0; i < cnt; i ++ ) { int p = primes[i]; int s = 0; for (int j = n; j >= 1; j /= p) s += j / p; ans = ans * (2 * s + 1) % mod; } cout << ans << endl; return 0; }
题目:
对于任何正整数 x,其约数的个数记作 g((x),例如 g(1)=1、g(6)=4。
如果某个正整数 x 满足:对于任意的小于 x 的正整数 i,都有 g(x)>g(i),则称 x 为反素数。
例如,整数1,2,4,6 等都是反素数。
现在给定一个数 N,请求出不超过 N 的最大的反素数。
输入格式
一个正整数 N。
输出格式
一个整数,表示不超过 N 的最大反素数。
数据范围
1≤N≤2∗109
输入样例:
1000
输出样例:
840
分析:
这里的代码还是要看以前的提交的,DFS还是不行,这时硬伤
代码:
#include <bits/stdc++.h> using namespace std; typedef long long ll; ll a[9] = {2, 3, 5, 7, 11, 13, 17, 19, 23}; ll mx, num; ll n; void dfs(ll u, ll last, ll val, ll cnt) { // printf("u = %lld, val = %lld, cnt = %lld\n", u, val, cnt); if (cnt > num || (cnt == num && val < mx)) { mx = val; num = cnt; } if (u == 9) return; for (int i = 1; i <= last; i ++ ) { if (val * a[u] > n) break; val *= a[u]; dfs(u + 1, i, val, cnt * (i + 1)); } } int main(){ cin >> n; dfs(0, 30, 1, 1); cout << mx << endl; return 0; }
题目:
Hanks 博士是 BT(Bio-Tech,生物技术)领域的知名专家,他的儿子名叫 Hankson。
现在,刚刚放学回家的 Hankson 正在思考一个有趣的问题。
今天在课堂上,老师讲解了如何求两个正整数 c1 和 c2 的最大公约数和最小公倍数。
现在 Hankson 认为自己已经熟练地掌握了这些知识,他开始思考一个“求公约数”和“求公倍数”之类问题的“逆问题”,这个问题是这样的:
已知正整数 a0,a1,b0,b1,设某未知正整数 xx 满足:
- x 和 a0 的最大公约数是 a1;
- x 和 b0 的最小公倍数是 b1。
Hankson 的“逆问题”就是求出满足条件的正整数 x。
但稍加思索之后,他发现这样的 x 并不唯一,甚至可能不存在。
因此他转而开始考虑如何求解满足条件的 x 的个数。
请你帮助他编程求解这个问题。
输入格式
输入第一行为一个正整数 n,表示有 n 组输入数据。
接下来的 n 行每行一组输入数据,为四个正整数 a0,a1,b0,b1,每两个整数之间用一个空格隔开。
输入数据保证 a0 能被 a1 整除,b1 能被 b0 整除。
输出格式
输出共 n 行。
每组输入数据的输出结果占一行,为一个整数。
对于每组数据:若不存在这样的 x,请输出 0;
若存在这样的 x,请输出满足条件的 x 的个数;
数据范围
1≤n≤2000,
1≤a0,a1,b0,b1≤2∗109输入样例:
2 41 1 96 288 95 1 37 1776
输出样例:
6 2
分析:
将b1所有约数求出来,然后枚举判断是否合法,同时DFS还是不行,这里的的FS需要看原来的代码才写出来的
代码:
#include <bits/stdc++.h> #define x first #define y second using namespace std; typedef long long ll; typedef pair<int, int> PII; const int N = 100006; int primes[N], cnt; bool st[N]; ll a0, a1, b0, b1; PII divi[100]; int tot; int dv[N], ct; void get_primes() { for (int i = 2; i <= N; i ++ ) { if (!st[i]) primes[cnt ++ ] = i; for (int j = 0; primes[j] * i <= N; j ++ ) { st[i * primes[j]] = 1; if (i % primes[j] == 0) break; } } } void dfs(int u, int p) { if (u > tot) { dv[++ ct] = p; return; } for (int i = 0; i <= divi[u].y; i ++ ) { dfs(u + 1, p); p *= divi[u].x; } } int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); } int lcm(int a, int b) { return 1ll * a * b / gcd(a, b); } void work() { cin >> a0 >> a1 >> b0 >> b1; ll t = b1; tot = 0; for (int i = 0; primes[i] <= t / primes[i]; i ++ ) { int p = primes[i], s = 0; if (t % p == 0) { while (t % p == 0 ) s ++, t /= p; divi[++ tot] = {p, s}; } } if (t > 1) divi[++ tot] = {t, 1}; ct = 0; dfs(1, 1); int res = 0; for (int i = 1; i <= ct; i ++ ) { int x = dv[i]; if (gcd(x, a0) == a1 && lcm(x, b0) == b1) res ++; } printf("%d\n", res); } int main() { int T; cin >> T; get_primes(); while (T -- ) { work(); } return 0; }