❗️ ❗️ ❗️
质数:所以大于1的自然数
如:2、3、4、5、······
质数的定义:
在大于1的整数中,如果只包含1和其本身这两个约数就被称为质数,或者叫素数。
所有<=1
的整数既不是质数也不是合数
质数和合数是针对所有大于1的数定义的
(1)所有不满足>1的筛掉
(2)枚举能被i整除的数
for(int i=2;i
从质数的性质出发
d|n-->n|d|n
d
能整除n
,说明n|d
也能整除n
。
枚举的终止条件次数减小到 d<=n/d
进一步化简式子:d*d<=n
即d<=sqrt(n)
枚举次数从n–>sqrt(n)
O(sqrt(n))
d<=n/d;
原因:
(1)d*d<=n
n的最大值为2147483647
当i*i
<=n
,n
接近最大值时:
下一次(i+1)*(i+1)
时大于最大值,就会出现溢出错误。
(i+1)x(i+1)
就会变为负数,造成结果错误。
(2)d<=sqrt(n)
每次枚举时都需要计算一遍sqrt(n)
,sqrt()
函数比较慢,耗时多,不推荐。
import java.util.*;
public class Main{
public static boolean primes(int n){
if(n<2)return false;
//不满足定义筛掉
for(int i=2;i<=n/i;i++){
//记住从2开始枚举到根号n结束
if(n%i==0)return false;
}
return true;
}
public static void main(String []args){
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
while(n-->0){
int a=sc.nextInt();
if(primes(a))System.out.println("Yes");
else System.out.println("No");
}
}
}
质因子:底数为质数
当枚举到i
时,意味着把2~i-1
的质因子都除干净了。
也就意味着i
不包含从2~i-1
的质因子
那么n
是i
的倍数,并且n、i
中不包含2~i-1
之间的所有质因子
那么i
一定是质数,不是合数
性质:
n
中最多只包含一个大于sqrt(n)
的质因子
证:
假设n
中包含两个大于sqrt(n)
的质因子,两个相乘必定大于n。
所以最多只存在一个大于sqrt(n)
的质因子。
试除法判定质数
时间复杂度一定是**O(sqrt(n))**
最好情况下是:O(logn)
最坏情况下是:O(sqrt(n))
时间复杂度介于O(logn)~O(sqrt(n)
之间,总体优于试除法判定质数。
import java.util.*;
public class Main{
public static void divide(int n){
for(int i=2;i<=n/i;i++){
if(n%i==0){
//找到质因子
int s=0;
//统计质因子的个数
while(n%i==0){
n/=i;
s++;
}
System.out.println(i+" "+s);
//i表示质数,s表示指数(质因子出现的次数)
}
}
if(n>1)System.out.println(n+" "+1);
//如果最后n大于1,则n为大于sqrt(n)的数。
//特判输出一下即可。
System.out.println();
}
public static void main(String []args){
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
while(n-->0){
int a=sc.nextInt();
divide(a);
}
}
}
从2~n中去枚举每个数,假设当前枚举到的数为P
,后面出现是P
的倍数的数均删掉,最后剩下的便是不存在倍数关系的质数,统计个数即可。
因为,假设某个数a是P的倍数,说明这个数除了1和它本身外,还能整除P,与质数的定义相悖,因此a不是质数,需要筛掉。
时间复杂度:O(nlogn)
耗时:850ms
import java.util.*;
public class Main{
static int N=1000010;
static int cnt;
static boolean st[]=new boolean[N];
public static void get_primes(int n) {
for(int i=2;i<=n;i++) {
//从2开始枚举,因为1不是质数
//依次枚举每个数,看是不是质数
if(!st[i])cnt++;
//如果最后没有被标记说明是质数
for(int j=i+i;j<=n;j+=i) {
st[j]=true;
//枚举每个数在2~n中是否存在i的倍数的数
//存在则打上标记,置为true,将他们都筛掉。
}
}
}
public static void main(String []args) {
Scanner scanner=new Scanner(System.in);
int n=scanner.nextInt();
get_primes(n);
System.out.println(cnt);
}
}
n
是质数时,将前面与n
有倍数约束关系的数字筛掉。时间复杂度:O(nloglogn)
耗时:782 ms
import java.util.*;
public class Main{
static int N=1000010;
static int primes[]=new int[N];
static boolean st[]=new boolean[N];
static int cnt=0;
public static void get_primes(int n){
for(int i=2;i<=n;i++){
if(!st[i]){
primes[cnt++]=i;
//不需要去枚举从2~n-1里的所有数
//当枚举的n是质数时,将前面与n有倍数约数关系的数字筛掉
//这样枚举的时候只需枚举质数即可,减少枚举的次数
for(int j=i+i;j<=n;j+=i)st[j]=true;
}
}
}
public static void main(String []args){
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
get_primes(n);
System.out.println(cnt);
}
}
n只会被最小质因子筛掉
primes[j]:
表示是i
的最小质因子
后面用**pj
**同义表示
pj
是从前往后去枚举,最先满足条件的一定是最小质因子。
pj
一定是i
的最小质因子,pj
一定是pj*i
的最小质因子
找不到质因子,说明**pj
一定小于** i
的所有质因子。
pj
一定小于i
的所有质因子,pj
一定是pj*i
的最小质因子
这样可以通过最小质因子筛掉i中有约束关系的数,剩余的便是质数
耗时:766 ms
import java.util.*;
public class Main{
static int N=1000010;
static int primes[]=new int[N];
static boolean st[]=new boolean[N];
static int cnt=0;
public static void get_primes(int n){
//依次枚举每个数字
for(int i=2;i<=n;i++){
if(!st[i])primes[cnt++]=i;
//如果说当前的i没有没被筛掉则说明他是质数
for(int j=0;primes[j]*i<=n;j++){
st[primes[j]*i]=true;
//存在i倍的关系则把他给筛掉
if(i%primes[j]==0)break;
//说明primes[j]一定是i的最小质因子,筛掉i
}
}
}
public static void main(String []args){
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
get_primes(n);
System.out.println(cnt);
}
}
import java.util.*;
public class Main{
public static int gcd(int a,int b){
while(b!=0){
int c=a%b;
a=b;
b=c;
}
return a;
}
public static void main(String []args){
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
while(n-->0){
int a=sc.nextInt();
int b=sc.nextInt();
System.out.println(gcd(a,b));
}
}
}
/*
组合数:C(a,b)
public static long C(long a,long b){
long res=1;
for(int i=a,j=1;j<=b;i--,j++){
res=res*i/j;
if(res>n)return res;
}
return res;
}
*/
反复平方法
a^k mod p
对k进行拆分
把他进行预处理出来
把k 从一个十进制数拆成若干个二进制数
再将a的幂数相加即可!
观察发现拆开后,后一项等于前一项的平方再%,总共是logk
项
借助这一点反复平方直至b
为0
时,说明此时的k
已无可再拆分的数为止。
本题输入数据比较大,java选手需要快读快写!!!
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.*;
public class Main{
static BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
static PrintWriter pW = new PrintWriter(new OutputStreamWriter(System.out));
public static long qmi(int a,int b,int p) {
long ans=1;
//开long
//反复平方法
while(b>0) {
if((b&1)==1)ans=ans*a%p;
//如果说末位为1则ans=a%p
b>>=1;
//再把这一位给删掉
a=(int)((long)a*a%p);
//反复平方法再取模
}
return ans;
}
public static void main(String []args) throws NumberFormatException, IOException {
int n=Integer.parseInt(bf.readLine());
while(n-->0) {
String s[]=bf.readLine().split(" ");
int a=Integer.parseInt(s[0]);
int b=Integer.parseInt(s[1]);
int p=Integer.parseInt(s[2]);
pW.println(qmi(a,b,p));
}
pW.flush();
}
}
递推式:C(a,b)=C(a-1,b)+C(a-1,b-1)
理解:假设我挑出一个苹果
当前包含该苹果,由于该苹果已经被我选了,还剩**a-1
件。
所以从剩余的b-1
**件苹果中选
当前不包含该苹果,苹果还剩**a-1
件。
这a-1
**件从b
件苹果中去选
import java.util.*;
public class Main{
static int N=2010;
static int mod=1000000007;
//写成1e9+7会报精度损失
//直接写数字即可
static int c[][]=new int[N][N];
public static void init(){
for(int i=0;i<N;i++){
for(int j=0;j<=i;j++){
if(j==0)c[i][j]=1;
//注意边界情况,从中选0个苹果的方案为1
else c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
//递推式,数字比较大需要取模
}
}
}
public static void main(String []args){
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
init();
while(n-->0){
int a=sc.nextInt();
int b=sc.nextInt();
System.out.println(c[a][b]);
}
}
}
✨ ✨ ✨
看到这里,不妨点个关注