P3383 【模板】线性筛素数
题目描述
P3383 【模板】线性筛素数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
运行代码
#include <iostream>
using namespace std;
typedef long long LL;
const int N = 100000010;
int vis[N]; //划掉合数
int prim[N]; //记录质数
int cnt; //质数个数
void Eratosthenes(int n){ //埃氏筛法
for(LL i=2; i<=n; ++i){
if(!vis[i]){
prim[++cnt] = i;
for(LL j=i*i; j<=n; j+=i)
vis[j] = 1;
}
}
}
int main(){
int n, q, k;
scanf("%d %d", &n, &q);
Eratosthenes(n);
while(q--){
scanf("%d", &k);
printf("%d\n", prim[k]);
}
return 0;
}
代码思路
函数定义:
Eratosthenes
函数使用埃氏筛法来筛选出小于等于n
的所有质数。- 从 2 开始遍历到
n
。 - 如果当前数字
i
未被标记为合数(即vis[i] == 0
),则将其记录为质数(存入prim
数组),然后将其倍数标记为合数(从i * i
开始,因为小于i * i
的倍数在之前的遍历中已经被标记了)。
- 从 2 开始遍历到
主函数 main
:
- 首先读取两个整数
n
和q
,分别表示要筛选的范围上限和查询的次数。 - 调用
Eratosthenes
函数筛选出小于等于n
的质数。 - 然后进行
q
次查询:- 每次读取一个数字
k
。 - 输出
prim[k]
,即第k
个质数。
- 每次读取一个数字
例如,如果输入 n = 10
, q = 2
,接下来输入 2
和 3
:
- 在
Eratosthenes
函数中,会筛选出 2、3、5、7 这几个质数,并记录在prim
数组中。 - 第一次查询
k = 2
,会输出prim[2]
,即 3 。 - 第二次查询
k = 3
,会输出prim[3]
,即 5 。
P5091 【模板】扩展欧拉定理
题目描述
P5091 【模板】扩展欧拉定理 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
运行代码
#include<iostream>
using namespace std;
typedef long long ll;
int a, b, m, phi, flag;
char s[20000005];
int get_phi(int n) {//求欧拉函数
int res = n;
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
res = res / i * (i - 1);
while (n % i == 0)
n /= i;
}
}
if (n > 1)
res = res / n * (n - 1);
return res;
}
int de_pow(int phi) {//降幂
int b = 0;
for (int i = 0; s[i]; i++) {
b = b * 10 + (s[i] - '0');
if (b >= phi)
flag = 1, b %= phi;
}
if (flag)
b += phi;
return b;
}
int quick_pow(ll a, int b) {//快速幂
int res = 1;
while (b) {
if (b & 1)
res = res * a % m;
a = a * a % m;
b >>= 1;
}
return res;
}
int main() {
scanf("%d%d%s", &a, &m, s);
phi = get_phi(m);
b = de_pow(phi);
printf("%d", quick_pow(a, b));
return 0;
}
代码思路
-
函数
get_phi(int n)
- 目的是计算输入整数
n
的欧拉函数值。 - 采用试除法,从
2
到sqrt(n)
遍历,如果n
能被i
整除,就逐步更新res
的值,并将n
除以i
直到不能整除。 - 最后,如果
n
仍大于1
,再进行一次更新。
- 目的是计算输入整数
-
函数
de_pow(int phi)
- 用于对输入的数字字符串
s
进行处理,将其转换为整数b
,并对b
进行降幂操作。 - 通过逐位读取字符串并累加计算
b
,在累加过程中如果b
超过了phi
,则进行取模并设置标志位flag
。 - 如果标志位
flag
被设置,说明有溢出,需要将b
加上phi
。
- 用于对输入的数字字符串
-
函数
quick_pow(ll a, int b)
- 实现快速幂算法,计算
a
的b
次幂对m
取模的结果。 - 通过位运算判断
b
的每一位,如果是1
则更新结果,同时不断平方a
并更新b
。
- 实现快速幂算法,计算
-
main
函数- 首先通过
scanf
函数获取输入的整数a
、m
和字符串s
。 - 调用
get_phi(m)
计算m
的欧拉函数值并存入phi
。 - 调用
de_pow(phi)
对字符串进行处理得到降幂后的b
。 - 最后调用
quick_pow(a, b)
计算并输出结果。
- 首先通过
例如,如果输入 a = 2
, m = 5
, s = "12"
,那么:
- 在
get_phi(5)
中,由于5
是质数,其欧拉函数值为4
。 - 在
de_pow(4)
中,b
计算为12 % 4 = 0
,由于没有溢出,flag
为0
。 - 在
quick_pow(2, 0)
中,返回1
作为最终结果。
HDU 2973——YAPTCHA(威尔逊定理)
题目描述
威尔逊定理
威尔逊定理的内容是:
对于任何大于1的整数n,有以下等价条件:
- n是一个素数;
- (n-1)! + 1 是n的倍数,即(n-1)! ≡ -1 (mod n)。
换句话说,如果n是素数,那么(n-1)! 加上1能被n整除;反之,如果(n-1)! 加上1能被n整除,那么n必定是一个素数。
例如,考虑素数5,有(5-1)! = 4! = 24。因为24 + 1 = 25,而25可以被5整除,所以满足威尔逊定理。但是,如果取的是合数9,计算(9-1)! = 8! = 40320,显然40320 + 1 = 40321不能被9整除,因此不满足威尔逊定理。
运行代码
#include <iostream>
using namespace std;
typedef long long LL;
const int N = 1000001;
const int mx = 3000008;
int s[N], p[N], vis[mx], t, n;
void get_prime() {
for (LL i = 2; i < mx; ++i)
if (!vis[i]) {
if ((i - 7) % 3 == 0)
p[(i - 7) / 3] = 1;
for (LL j = i * i; j < mx; j += i)
vis[j] = 1;
}
}
int main() {
get_prime();
for (int i = 2; i < N; ++i)
s[i] = s[i - 1] + p[i];
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
printf("%d\n", s[n]);
}
}
代码思路
-
初始化类型和常量:
LL
定义为long long
类型,用于处理大整数运算。N
和mx
定义为数组大小和筛法的上限,分别用于存储累计素数计数和素数检测范围。
-
get_prime()
函数:- 使用布尔数组
vis[]
标记非素数。 - 遍历从2到
mx
的每个数字,如果i
未被标记,则它是素数:- 检查
(i - 7) % 3 == 0
是为了筛选出形如6k±1的素数。由于2和3已经是已知的素数,我们只关心那些形如6k±1的素数,它们满足(i - 7) % 3 == 0
。 - 标记
i
的所有倍数为非素数。
- 检查
- 结果存储在
p[]
数组中,其中p[(i - 7) / 3] = 1
表示i
是形如6k±1的素数。
- 使用布尔数组
-
主函数逻辑:
- 使用累积和数组
s[]
来记录小于或等于i
的所有形如6k±1的素数数量。 s[i] = s[i - 1] + p[i];
这行代码实现了累积和,使得s[i]
包含小于或等于i
的所有符合条件的素数数量。- 输入一个整数
t
,表示需要查询的次数。 - 对于每次查询,读入一个整数
n
,输出小于或等于n
的所有形如6k±1的素数的数量,即s[n]
。
- 使用累积和数组
注意点:
- 筛法的效率依赖于
mx
的大小,较大的mx
意味着更多的内存使用和更长的初始化时间。 s[]
数组用于快速查询,避免了重复计算,提高了查询效率。