P3383 【模板】线性筛素数、P5091 【模板】扩展欧拉定理、HDU 2973——YAPTCHA(威尔逊定理)

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 的倍数在之前的遍历中已经被标记了)。

主函数 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;
}

代码思路

  1. 函数 get_phi(int n)

    • 目的是计算输入整数 n 的欧拉函数值。
    • 采用试除法,从 2 到 sqrt(n) 遍历,如果 n 能被 i 整除,就逐步更新 res 的值,并将 n 除以 i 直到不能整除。
    • 最后,如果 n 仍大于 1,再进行一次更新。
  2. 函数 de_pow(int phi)

    • 用于对输入的数字字符串 s 进行处理,将其转换为整数 b ,并对 b 进行降幂操作。
    • 通过逐位读取字符串并累加计算 b ,在累加过程中如果 b 超过了 phi ,则进行取模并设置标志位 flag 。
    • 如果标志位 flag 被设置,说明有溢出,需要将 b 加上 phi 。
  3. 函数 quick_pow(ll a, int b)

    • 实现快速幂算法,计算 a 的 b 次幂对 m 取模的结果。
    • 通过位运算判断 b 的每一位,如果是 1 则更新结果,同时不断平方 a 并更新 b 。
  4. 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(威尔逊定理)

题目描述

Problem - 2973 (hdu.edu.cn)

威尔逊定理

威尔逊定理的内容是:

对于任何大于1的整数n,有以下等价条件:

  1. n是一个素数;
  2. (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]);
    }
}

代码思路

  1. 初始化类型和常量:

    • LL定义为long long类型,用于处理大整数运算。
    • Nmx定义为数组大小和筛法的上限,分别用于存储累计素数计数和素数检测范围。
  2. 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的素数。
  3. 主函数逻辑:

    • 使用累积和数组s[]来记录小于或等于i的所有形如6k±1的素数数量。
    • s[i] = s[i - 1] + p[i];这行代码实现了累积和,使得s[i]包含小于或等于i的所有符合条件的素数数量。
    • 输入一个整数t,表示需要查询的次数。
    • 对于每次查询,读入一个整数n,输出小于或等于n的所有形如6k±1的素数的数量,即s[n]

注意点:

  • 筛法的效率依赖于mx的大小,较大的mx意味着更多的内存使用和更长的初始化时间。
  • s[]数组用于快速查询,避免了重复计算,提高了查询效率。
  • 14
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

筱姌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值