质数,快速幂,逆元

质数,快速幂,逆元

记录一下最近学的一些模板,还有一些例题。免得忘记了,还是自己常用的模板最好看哈哈

1:质数

质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。

求法一:埃氏筛:从2开始,将每个质数的倍数都标记成合数,以达到筛选素数的目的。

复杂度近似 O(n),求 10^7内的质数没有问题。

【模板】:

typedef long long ll;
const int maxn=1e6+7;
int prime[maxn];
//将每个质数的倍数都标记为1,剩下的未标记为1的就是质数 
void  isprime()
{
	prime[0]=prime[1]=1;
	for(ll i=2;i*i<=maxn;i++){
		if(!prime[i])//判断是否有被标记过
		{
			for(ll j=i*i;j<maxn;j+=i)
			    prime[j]=1;//注意是将不是质数的标记为1了 
		} 
	}
} 

主函数调用的时候,注意判断是质数是if(!prime[i]) …

求法二:欧拉筛:在埃氏筛法的基础上,让每个合数只被它的最小质因子筛选一次

【模板】:

typedef long long ll;
const int maxn=1e6+7;
int prime[maxn],primesize;//prime存放范围里面所有的质数,primesize表这个范围内质数有多少个
bool isprime[maxn];
void getlist(int listsize)
{
	isprime[0]=isprime[1]=1;
	for(ll i=2;i*i<=listsize;i++)
	{
		if(!isprime[i]) prime[++primesize]=i;//未被标记过,表明是质数 
		for(ll j=1;j<=primesize&&i*prime[j]<=listsize;j++)
		{
			isprime[i*prime[j]]=1;
			if(i%prime[j]==0) break;
		} 
	}
} 
例题一:洛谷p3383【模板】线性筛素数
题目描述

如题,给定一个范围 n,有 q个询问,每次输出第 k 小的素数。

输入格式

第一行包含两个正整数 n,q,分别表示查询的范围和查询的个数。

接下来 q行每行一个正整数 k,表示查询第 k小的素数。

输出格式

输出 q行,每行一个正整数表示答案。

**输入 **

100 5
1
2
3
4
5

**输出 **

2
3
5
7
11

代码:

#include<bits/stdc++.h>
using namespace std;
int prime[100000005],primesize;//存放范围内所有的质数
int isprime[100000005];//配合埃氏筛

void getlist(int listsize)
{
	isprime[0]=isprime[1]=1;//将不是质数的标记为1
	for(int i=2;i<=listsize;i++)
	{
		//如果没有标记过,即为质数,存放在isprime[]数组里面 
		if(!isprime[i]) prime[++primesize]=i;
		for(int j=1;j<=primesize&&i*prime[j]<=listsize;j++)
		{
			isprime[i*prime[j]]=1;//将质数的倍数标记一下,表示不为质数了 
			if(i%prime[j]==0) break;//优化 
		} 	 
	} 
}

//快读一波
inline int read()
{
	int x=0,f=1; char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') f=-1; ch=getchar();
	} 
	while(ch>='0'&&ch<='9'){ x=x*10+ch-'0'; ch=getchar();
	}
	return x*f;
}

int main()
{
	int n,q;
	n=read();
	q=read();
	getlist(n); 
	int tp;
	while(q--)
	{
		tp=read();
		cout<<prime[tp]<<endl;
	}
	return 0;
} 
例题二:p5723【深基4.例13】质数口袋
题目描述

小 A 有一个质数口袋,里面可以装各个质数。他从 22 开始,依次判断各个自然数是不是质数,如果是质数就会把这个数字装入口袋。口袋的负载量就是口袋里的所有数字之和。但是口袋的承重量有限,不能装得下总和超过 L(1≤L≤10^5)的质数。给出 LL,请问口袋里能装下几个质数?将这些质数从小往大输出,然后输出最多能装下的质数个数,所有数字之间有一空行。

输入格式

一行一个正整数 L。

输出格式

将这些质数从小往大输出,然后输出最多能装下的质数个数,所有数字之间有一空行。

**输入 **

100

**输出 **

2
3
5
7
11
13
17
19
23
9

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=200005;
int prime[maxn];
void isprime()
{
	prime[0]=prime[1]=1;
	for(long long i=2;i<=maxn;i++)
	{
		if(!prime[i])
		{
			for(long long j=i*i;j<=maxn;j+=i)
			{
				prime[j]=1; 
			} 
		}
	}
}
inline int read()
{
	int x=0,f=1; char ch=getchar();
	while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar();
	}
	while(ch>='0'&&ch<='9'){ x=x*10+ch-'0'; ch=getchar();
	}
	return x*f;
 } 
int main()
{
	int x,sum=0;
	x=read();
	isprime();
	for(int i=1;i<=x;i++)
	{
		if(!prime[i])
		{
			cout<<i<<endl;
			x-=i;
			sum++;
		}
		if(x<=0)  break;
		 
	}
    cout<<sum<<endl;
	return 0;
 } 
例题三:P1217 [USACO1.5]回文质数 Prime Palindromes
题目描述

因为 151 既是一个质数又是一个回文数(从左到右和从右到左是看一样的),所以 151 是回文质数。

写一个程序来找出范围 [a,b] (5≤a<b≤100,000,000)( 一亿)间的所有回文质数。

输入格式

第 1 行: 二个整数 a 和 b .

输出格式

输出一个回文质数的列表,一行一个。

**输入 **

5 500

**输出 **

5
7
11
101
131
151
181
191
313
353
373
383
除了11以外,所有回文素数的位数都是奇数。这点可以省去很多时间

道理很简单:如果一个回文素数的位数是偶数,则它的奇数位上的数字和与偶数位上的数字和必然相等;根据数的整除性理论,容易判断这样的数肯定能被11整除,所以它就不可能是素数。

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=100000005;
//埃氏筛
int prime[maxn];
void isprime(int n)
{
	prime[0]=prime[1]=1;
	for(int i=2;i*i<=n;i++)
	{
		if(!prime[i])//没标记过
		{
			for(int j=i*i;j<=n;j+=i)
			{
				prime[j]=1;//不是质数的标记一下 
			} 
		} 
	}
 } 

int hw(int x)
{
	int y=x,num=0;
	//把倒着看的数,计算出来,然后比较一下 
	while(y!=0)
	{
		num=num*10+y%10;
		y/=10;
	}
	if(num==x) return 1;
	else return 0;
}
inline int read()
{
	int n=0,f=1; char ch=getchar();
	while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar();
	}
	while(ch>='0'&&ch<='9'){ n=n*10+ch-'0'; ch=getchar();
	}
	return n*f;
}
int main()
{
	int a,b;
	a=read(); b=read();
	if(b>=10000000)  b=9999999;//偶数数位除了11,不可能为回文质数 
	isprime(b);//预处理
	
	if(a%2==0) a++;//偶数不可能是质数,保证循环里面每一位都是奇数 
	for(int i=a;i<=b;i+=2)//偶数不可能是质数 
	{
		if(!prime[i]&&hw(i))//是质数并且是回文数
		cout<<i<<endl; 
	}
	return 0; 
}
例题四:P1579 哥德巴赫猜想(升级版)
题目描述

现在请你编一个程序验证哥德巴赫猜想。

先给出一个奇数n,要求输出3个质数,这3个质数之和等于输入的奇数。

输入格式

仅有一行,包含一个正奇数n,其中9<n<20000

输出格式

仅有一行,输出3个质数,这3个质数之和等于输入的奇数。相邻两个质数之间用一个空格隔开,最后一个质数后面没有空格。如果表示方法不唯一,请输出第一个质数最小的方案,如果第一个质数最小的方案不唯一,请输出第一个质数最小的同时,第二个质数最小的方案。

**输入 **

2009

**输出 **

3 3 2003

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=20005;
int prime[maxn];

//埃氏筛 
void isprime(){
	prime[0]=prime[1]=1;
	for(long long i=2;i<maxn;i++)
	{
		if(!prime[i])//判断是否标记过
		{
			for(long long j=i*i;j<maxn;j+=i)
			prime[j]=1;//注意这里是把不是质数的标记了 
		} 
	}
} 

int main()
{
	int n;
	cin>>n;
	//将质数标记一下 
	isprime();
	//如果n-4 是质数的话,那直接输出 2 2 n-4 就好啦~ 
	if(!prime[n-4]) {cout<<2<<" "<<2<<" "<<n-4<<endl;return 0;} 
	
	//如果n-4不是的话 暴力求解啦~三个质数,双重循环就好了 
	for(int i=3;i*i<=n;i+=2)
	{
		if(!prime[i])
		{
			for(int j=i;j*j<=n;j+=2)
			{
				if(!prime[j]&&!prime[n-i-j])
				{ 
						 
					cout<<i<<" "<<j<<" "<<n-i-j<<endl;
					return 0;
				}
			}
		}
	}
	return 0;
}
例题五:P3912 素数个数
题目描述

求 1,2,⋯,N 中素数的个数。

输入格式

一行一个整数 N。

输出格式

一行一个整数,表示素数的个数。

**输入 **

10

**输出 **

4

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=100000005;
bool prime[maxn];//用int会超内存哈哈,以后还是尽量用bool吧
void isprime(int n)
{
	prime[0]=prime[1]=1;
	for(int i=2;i*i<=n;i++)
	{
		if(!prime[i])
		{
			for(int j=i*i;j<=n;j+=i)
			{
				prime[j]=1;//标记一下 
			}
		}
	}
}
int main()
{
	int n;
	cin>>n;
	isprime(n);
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		if(!prime[i])
		ans++;
	}
	cout<<ans<<endl;
	return 0;
}
例题六:P1865 A % B Problem
题目描述

给定 l, r求区间 $[l, r] $内质数的个数。

输入格式

第一行有两个整数,分别代表询问次数 n和 给定区间的右端点最大值 m。

接下来 n行,每行两个整数 l, r代表一次查询。

输出格式

对于每次查询输出一行,若$ l, r \in [1, m]$,则输出区间质数个数,否则输出 Crossing the line

**输入 **

2 5
1 3
2 6

**输出 **

2
Crossing the line

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1000005;
int prime[maxn];
void isprime(int n)
{
	prime[0]=prime[1]=1;
	for(int i=2;i*i<=n;i++)
	{
		if(!prime[i])
		{
			for(int j=i*i;j<=n;j+=i)
			{
				prime[j]=1;//标记一下 
			}
		}
	}
}
int main()
{
	int n,m;
	cin>>n>>m;
	isprime(m);//范围内预处理
	while(n--)
	{
		int l,r,ans=0;
		cin>>l>>r;
        //先注意判断不在范围的情况
		if(!(l>=1&&r<=m)) {cout<<"Crossing the line"<<endl;continue;
		} 
		for(int i=l;i<=r;i++)
		{
			if(!prime[i]) ans++;
		}
		cout<<ans<<endl; 
	} 
	return 0;
 } 

2:快速幂

emmm一句话就是利用二进制的一些性质求x^y的更快速的方式

【模板】:

typedef long long ll;
long long mod;
long long fastpow(long long x,long long y)
{
	x%=mod;//取模
	long long res=1;
	while(y)
	{
		if(y&1) res=res*x%mod;//习惯性取模
		y>>=1;
		x=x*x%mod; //继续取模
	}
	return res%mod; //绝不忘记取模
}
例题一:P1226 【模板】快速幂||取余运算
题目描述

给你三个整数 b,p,k,求 b p m o d k b^pmodk bpmodk

输入格式

输入只有一行三个整数,分别代表 b,p,k

输出格式

输出一行一个字符串 b^p mod k=s,其中 b, p, k分别为题目给定的值, s为运算结果。

**输入 **

2 10 9

**输出 **

2^10 mod 9=7

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; 
ll b,p,mod;
ll fastpow(ll x,ll y)
{
	x%=mod;
	ll res=1;
	while(y)
	{
		if(y&1) res=res*x%mod;
		y>>=1;
		x=x*x%mod; 
	}
	return res%mod; 
}
int main()
{ 
	cin>>b>>p>>mod;
	ll ans;
	ans=fastpow(b,p);
    cout<<b<<"^"<<p<<" "<<"mod"<<" "<<mod<<"="<<ans<<endl;
    return 0;
}

3:逆元

定义:若 a ∗ x ≡ 1 ( m o d b ) a∗x≡1(modb) ax1(modb),且a与b互质,那么我们就能定义:
x 为 a 的逆元,记为 a − 1 a^-1 a1,所以我们也可以称 x 为 a 在 mod b意义下的倒数,

所以对于 $a/b(mod p) $,我们就可以求出 b 在 mod p下的逆元,然后乘上 a,再 mod p,就是这个分数的值了。

额,是不是看不懂,没事,我也看不懂。但是我们还是要记得咋搞

求法一:费马小定理(记住 n在模 p 下的逆元是 n^(p-2) 就好了) O ( l o g n ) O(log n) O(logn)
typedef long long ll;
int n,mod;
ll fastpow(ll x,ll y)
{
	x%=mod;
	ll res=1;
	while(y){
		if(y&1) res=res*x%mod;
		y>>=1;
		x=x*x%mod;
	}
	return res%mod;
}
ll inv(ll n){
	return fastpow(n,mod-2);//n^(mod-2)
}
求法二:线性求逆元(记住一个公式 i n v i ≡ − ⌊ P / i ⌋ ∗ i n v ( P   m o d   i ) ( m o d   P ) invi≡−⌊P/i⌋∗inv(P\ mod\ i)(mod\ P) inviP/iinv(P mod i)(mod P)就好了) O ( N ) O(N) O(N)
//求1~n的 i在模p下的乘法逆元。
typedef long long ll;
inv[1]=1;//1的逆元都是1
for(int i=2;i<=n;i++)
inv[i]=(((ll)(-p/i)*inv[p%i])%p+p)%p;//
例题一:P3811 【模板】乘法逆元
题目描述

给定 n,p 求$ 1\sim n$ 中所有整数在模 p 意义下的乘法逆元。

输入格式

一行两个正整数 n,p。

输出格式

输出 n行,第 i 行表示 i 在模 pp 下的乘法逆元。

**输入 **

10 13

**输出 **

1
7
9
10
8
11
2
5
3
4

用费小马定理T掉三个点,wa果然还是用线性更快一点哈哈

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=3e6+10;
int n,p,inv[maxn];
//快读一波
inline int read()
{
	int x=0,f=1; char ch=getchar();
	while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar();
	}
	while(ch>='0'&&ch<='9'){ x=x*10+ch-'0' ; ch=getchar();
	}
	return x*f;
}
int main()
{
	n=read();p=read();
	inv[1]=1; printf("1\n");
	for(int i=2;i<=n;i++)
	{
		inv[i]=(((ll)(-p/i)*inv[p%i])%p+p)%p;
		printf("%d\n",inv[i]); 
	}
	return 0;
}
例题二:P5431 【模板】乘法逆元2
题目描述

给定 n个正整数 a i a_i ai ,求它们在模 p 意义下的乘法逆元。
由于输出太多不好,所以将会给定常数 k,你要输出的答案为:

∑ i = 1 n k i a i \sum\limits_{i=1}^n\frac{k^i}{a_i} i=1naiki

当然要对 p 取模。

输入格式

第一行三个正整数 n,p,k,意义如题目描述。
第二行 n个正整数 a i a_i ai,是你要求逆元的数。

输出格式

输出一行一个整数,表示答案。

输入

6 233 42
1 4 2 8 5 7

输出

91

如果傻傻的直接暴力的话(把每一位的 k i a i \frac{k^i}{a_i} aiki的结果求出来,加起来,输出)T掉两个点哈哈

那么,我们为啥要把 一个个分母的逆元乘上分子(即每一项的值)求出来再加上去嘞,可以先边输入边通分,算到最后的结果了,再求分母逆元和分子相乘然后输出呀

k 1 a 1 \frac{k^1}{a_1} a1k1+ k 2 a 2 \frac{k^2}{a_2} a2k2= a 2 ∗ k 1 + a 1 ∗ k 2 a 1 ∗ a 2 \frac{a_2*{k^1}+a_1*{k^2}}{a_1*a_2} a1a2a2k1+a1k2将分子分母的值分别记录下来,用于下一项的通分。哦~对了,还得记录

k i k^i ki直到求到 k i a i \frac{k^i}{a_i} aiki,将所有的项求出来,最后再根据费马小定理求出分母的逆元,乘上分子,取模即可

具体看代码吧:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,mod,k,sum,a,x,y=1,k1;//x是分子,y是分母 
ll fastpow(ll x,ll y)
{
	x%=mod;
	ll res=1;
	while(y){
		if(y&1) res=res*x%mod;
		y>>=1;
		x=x*x%mod;
	}
	return res%mod;
}

inline int read()
{
	int n=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar();
	}
	while(ch>='0'&&ch<='9'){ n=n*10+ch-'0'; ch=getchar();
	}
	return n*f;
}
int main()
{
	n=read();mod=read();k=read();
	k1=k%mod;
	for(int i=1;i<=n;i++ )
	{
		a=read();
		x=(x*a+y*k1)%mod;//目前分子的值 
		 k1=k1*k%mod;//k^n
		y=y*a%mod;//分母的值a1*a2*a3*... 
		 
	}
	cout<<x*fastpow(y,mod-2)%mod<<endl;
	return 0;
}
 

代码都非常朴素哈,觉得友好的请来一个友好的赞

╭︿︿︿╮
{/-★★-/}
( (oo) )
︶︶︶

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值