质数,快速幂,逆元
记录一下最近学的一些模板,还有一些例题。免得忘记了,还是自己常用的模板最好看哈哈
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)
a∗x≡1(modb),且a与b互质,那么我们就能定义:
x 为 a 的逆元,记为
a
−
1
a^-1
a−1,所以我们也可以称 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) invi≡−⌊P/i⌋∗inv(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=1∑naiki
当然要对 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} a1∗a2a2∗k1+a1∗k2将分子分母的值分别记录下来,用于下一项的通分。哦~对了,还得记录
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) )
︶︶︶