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;
}
浙公网安备 33010602011771号