基礎數論知識一些總結

快速冪

在這裏插入圖片描述
非遞歸版

import java.util.Scanner;
public class Main {
	public static void main(String[] args) {
		// TODO 自動生成的方法存根
		Scanner sc=new Scanner(System.in);
		int t=sc.nextInt();
		for(int i=0;i<t;i++)
		{
			long b=sc.nextLong();
			long c=1000000007;long a=2;
			long res = 1;
	        a %= c;
	        for (; b != 0; b /= 2) {
	            if (b % 2 == 1)
	                res = (res * a) % c;
	            a = (a * a) % c;
	        }
			System.out.println(res);		
		}
	}
}
複製代碼

遞歸版(用的比較多)php

import java.util.Scanner;
public class Main {
   static long c=1000000007;
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int t=sc.nextInt();
		for(int i=0;i<t;i++)
		{
			long a=2;
			long b=sc.nextLong();			
			long value=divide((long)2,b);
			System.out.println(value%c);
		}
	}
	private static long divide(long a,long b) {
		if(b==0)return 1;
		else if(b%2==0) {return divide((a%c)*(a%c),b/2)%c;}
		else return a%c*divide((a%c)*(a%c),(b-1)/2)%c;
	}
}
複製代碼

矩陣快速冪

題目連接 核心思想爲: java

在這裏插入圖片描述
從右往左。能夠一直遞推,而後到最後一項,而後快速冪求矩陣,矩陣最終的結果就是所求結果。 更新java的矩陣通用乘法能夠表示爲,能夠將下列代碼替換道ac代碼中:

static int [][] multiplication(int a[][],int b[][]){//
	  int x=a.length;//a[0].length=b.length 爲知足條件
	  int y=b[0].length;//肯定每一排有幾個
	  int c[][]=new int [2][2];
	  for(int i=0;i<x;i++)
		  for(int j=0;j<y;j++)
		  {
			  //須要肯定每個元素
			  //c[i][j];
			  for(int t=0;t<b.length;t++)
			  {
				  c[i][j]+=(a[i][t]%10000)*(b[t][j]%10000);
				  c[i][j]%=10000;
			  }
		  }
	  
	   return c;  
  }
複製代碼
import java.util.Scanner;
public class Main {
	public static void main(String[] args) {
		// TODO 自動生成的方法存根
		Scanner sc=new Scanner(System.in);		
		while(sc.hasNext())
		{
		int n=sc.nextInt();
		if(n==-1)break;
		if(n==0) {System.out.println(0);}
		else {
		n-=1;//
		int a[][]= {{1,1},{1,0}};
		int b[][]={{1,0},{0,1}};//單位矩陣
	    int time=0;
		while(n>0)
		{
		    if(n%2==1)
			{
		    	b=q(a, b);
			}	
		    a=q(a, a);n/=2;
		}
		
		System.out.println((b[0][0]));
		}
		}
	}
static int [][] q(int a[][],int b[][]){//
	  int value1=a[0][0]*b[0][0]%10000+a[0][1]*b[1][0]%10000;//左上
	  int value2=a[0][0]*b[0][1]%10000+a[0][1]*b[1][1]%10000;//左上
	  int value3=a[1][0]*b[0][0]%10000+a[1][1]*b[1][0]%10000;//左上
	  int value4=a[1][0]*b[0][1]%10000+a[1][1]*b[1][1]%10000;//左上
	  int c[][]=new int [2][2];
	  c[0][0]=value1%10000;
	  c[0][1]=value2%10000;
	  c[1][0]=value3%10000;
	  c[1][1]=value4%10000;
	   return c;  
  }
}
複製代碼

拓展歐幾里得

拓展歐幾里得模板 參考:哈爾濱理工大學ACM培訓資料彙編/ACM-ICPC培訓資料彙編* 基本原理 :設 a 和 b 不全爲 0,==則存在整數 x,y 使得 xa+yb=gcd(a,b)=c== 對於展轉相除法的最後一項 此時 b=0,則 gcd(a,b)=1a+0b,(這個a,b是通過gcd的最後一項a,b)ios

由於gcd(a,b)=gcd(b,a%b)c++

則有x *a+y *b=x1 *b+y1 * (a%b) 將等式右邊變形,git

b *x1+(a%b) *y1=b *x1+(a-(a/b) *b) *y1=a *y1+b *(x1-(a/b) *y1)github

則,x=y1,y=x1-(a/b) *y1 則可由後向前迭代獲得 x,y算法

解題思路 :數組

對於擴展歐幾里德定理的題,通常都須要進行必定的推導以後獲得一個形式爲 xa+yb=c 的方程,而後根據 c 肯定解是否存在, 若是 c 能夠被 gcd(a,b)整除,那麼方程有 解,不然方程無解。 並且所得的解是不惟一的,對於一組解 x0,y0 則其全部解能夠表示爲 x=x0+b/gcd(a,b)*t,y=y0-a/gcd(a,b)*t,t=0,+1,+2……通常會要求找出 x 或者 y 的最小正整數 解,這個時候須要作一些調整。bash

總的來講。遞歸是一來一回的過程,在gcd中,咱們只用到了去的過程,求的最大公約數,而在exgcd中,咱們發現了在來的過程當中,某些數按照必定的規則變化,能夠獲得咱們想要的結果而已。markdown

static long x=0,y=0;
...
static long extgcd(long a,long b) {
		if(b==0) {x=1;y=0;return a;}
		long ans=extgcd(b, a%b);
		long team=x;
		x=y;
		y=team-a/b*y;
		return ans;
	}
複製代碼

求逆元: 從拓展歐幾里得中知道能夠求ax+by=c,通常是解決這類問題

當a,b互質時候。c必定等於1.由於gcd(a,b)=1,c必須被gcd整除,那麼若是有解必定是1.

那麼ax+by=1.

求逆元通常形式(求a關於1mod m的逆元)

ax≡1 (mod m). x就是所求的逆元

變形爲 ax+bm=1

這樣就能夠運用拓展歐幾里得(可是x要大於0處理)

static long x=0;static long y=0;//全局變量
  ......
   long q=tgcd(a,b);
           // long x1=x;
   if(1%q!=0) {out.println("Not Exist");}
   else {
       while(x<=0){//x*a+y*b=1 要求x>0這樣而且要求x最小,那麼這樣操做就至關於+ab-ab操做。剛開始還沒明白
             x+=b;
             y-=a;
         }
    system.out.println(x);}//

 static long tgcd(long a,long b) {
        if(b==0) {x=1;y=0;return a;}
        long ans=tgcd(b,a%b);
        long team=x;
        x=y;
        y=team-a/b*y;
       // System.out.println(x);
        return ans;

    }
複製代碼

還有用小費馬能夠求逆元,就不介紹了。

例題

杭電2669 給a,b求Xa + Yb = 1.若是沒有則輸出sorry。 能夠經過拓展歐幾里得指導Xa + Yb = gcd(a,b). 不言而喻要判斷gcd(a,b)是否等於1.若是不等於1,那麼就是sorry。若是等於一,那麼還不能讓x小於0,要對x,y進行加減操做知足x>0;拓展歐幾里得是經過遞歸從下往上進行運算。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;

/*
 * 拓展歐幾里得
 */
public class hdu2669 {
      static long x=0;static long y=0;
	public static void main(String[] args) throws IOException {
		// TODO 自動生成的方法存根
		StreamTokenizer in=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
		PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
		while(in.nextToken()!=StreamTokenizer.TT_EOF)
		{
			long a=(long)in.nval;
			in.nextToken();
			long b=(long)in.nval;
			long q=tgcd(a,b);
			if(1%q!=0) {out.println("sorry");}//gcd要和要求相等(這裏等於1)
			else {
				while(x<=0){//x*a+y*b=1  要求x>0這樣而且要求x最小,那麼這樣操做就至關於+ab-ab操做。剛開始還沒明白
					x+=b;
					y-=a;
				}
				out.println(x+" "+y);}//
			out.flush();
		}		
	}
	static long tgcd(long a,long b)
	{
		if(b==0) {x=1;y=0;return a;}
		long ans=tgcd(b,a%b);
		long team=x;
		x=y;
		y=team-a/b*y;
		return ans;
		
	}
}
複製代碼

題目:

兩隻青蛙在網上相識了,它們聊得很開心,因而以爲頗有必要見一面。它們很高興地發現它們住在同一條緯度線上,因而它們約定各自朝西跳,直到碰面爲止。但是它們出發以前忘記了一件很重要的事情,既沒有問清楚對方的特徵,也沒有約定見面的具體位置。不過青蛙們都是很樂觀的,它們以爲只要一直朝着某個方向跳下去,總能碰到對方的。可是除非這兩隻青蛙在同一時間跳到同一點上,否則是永遠都不可能碰面的。爲了幫助這兩隻樂觀的青蛙,你被要求寫一個程序來判斷這兩隻青蛙是否可以碰面,會在何時碰面。

咱們把這兩隻青蛙分別叫作==青蛙A和青蛙B==,而且規定緯度線上東經0度處爲原點,由東往西爲正方向,單位長度1米,這樣咱們就獲得了一條首尾相接的數軸。設青蛙A的出發點座標是x,青蛙B的出發點座標是y。青蛙A一次能跳m米,青蛙B一次能跳n米,兩隻青蛙跳一次所花費的時間相同。緯度線總長L米。如今要你求出它們跳了幾回之後纔會碰面。 Input

輸入只包括一行5個整數x,y,m,n,L,其中x≠y < 2000000000,0 < m、n < 2000000000,0 < L < 2100000000。 Output 輸出碰面所須要的跳躍次數,若是永遠不可能碰面則輸出一行"Impossible" Sample Input 1 2 3 4 5 Sample Output 4

分析

拓展歐幾里得之前記過

分析一下設見面時間爲t。青蛙A的位置是x+mt;青蛙B的位置是y+nt;兩隻青蛙項目,那麼說明兩隻青蛙之間要麼誰比誰多整數圈數。那麼就獲得x+mt-(y+nt)=k*L.變形一下(m-n)t-kL=(y-x).這就是m-n,L,y-x分別已知,而t和k未知,求最少的t.(t越少跳的越少。)

那麼設m-n=a,L=b,t=X,-k=Y,y-x=c.原始就是aX+bY=c,其中a,b,c已知,求X最小正整數。你要判斷c是否與gcd(a,b)互質,若是不互質則沒有結果。

接着你求出的初始X是gcd(a,b)的狀況,你要判斷c是不是gcd的倍數,若是跟他互質,那麼涼涼,不可能遇到,不知足extgcd的條件,若是是倍數。假設n倍,你能夠你能夠nXa,nYa,c至關於同時擴大倍的一種解,可是這不必定是最優解,你須要根據實際加減操做找到最小正解!

ac代碼

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;

public class poj1061 {

	static long X=0; static long Y=0;
	public static void main(String[] args) throws IOException {
		// TODO 自動生成的方法存根
		StreamTokenizer in=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
		PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
		in.nextToken();long x=(long)in.nval;//A起始位置
		in.nextToken();long y=(long)in.nval;//B起始位置
		in.nextToken();long m=(long)in.nval;//A的速率
		in.nextToken();long n=(long)in.nval;//B的速率
		in.nextToken();long L=(long)in.nval;//長度

		long a=m-n;
		long c=y-x;
		long b=L;
		if(a<0) {a=-a;c=-c;}
		long res=extgcd(a,b);// res=gcd(a,b)
		//c必須是res的倍數,若是互質的話就不知足拓展歐幾里得的方程式,而對應的結果首先要跟着倍數擴大
		if(c%res!=0) {out.println("Impossible");}
		else {
			/* * 可能難理解一點 * x=x0+(b/gcd(a,b))*t * x=x0+(b/res)*t找到最小的正整數x,那麼就是x%(b/res)了,若是小於0就是(x%b/res)+b/res了 */
			 X=X*(c/res);
            long t=b/res;
            if(X>=0)
                X=X%t;
            else
                X=X%t+t;
		    out.println(X);
		}
		out.flush();

	}
	private static long extgcd(long a, long b) {
		if(b==0)
		{
			X=1;Y=0;
			return a;
		}
		long res=extgcd(b, a%b);
		long team=X;
		X=Y;
		Y=team-(a/b)*Y;
		return res;
	}
}
複製代碼

github地址

素數篩

在斷定素數的問題中,隨着不斷學習,裏面的拓展性也在不斷地增長。

問題:斷定一個數是否爲素數:

想都不想的方法:

static boolean isprime(int value){
  for(int i=2;i<value;i++)
	{
	   if(value%i==0)
	   {return false;}
	}
	return true;
}
複製代碼

暴力從開始判斷的方法。複雜度O(n)。當數據大於1e7左右基本就爆了。更別說多組輸入了

稍微思考數據組成

對於一個數n若是==不是素數==,必定有a*b=n(a<b);a<根號n,b>根號n,因此只要出現必定是成雙成對。因此你只要==找到1個==就能說明這個數不是素數。因此從開始遍歷到a是最省時的方法。由於a是log級。算法複雜度爲O(logn)因此代碼爲:

static boolean isprime(int value) {
  for(int i=2;i*i<value+1;i++)
	{
	   if(value%i==0)
	   {return false;}
	}
	return true;
}
複製代碼

這種狀況可以基本單輸入的素數問題。可是遇到多個輸入確定也會GG的。

埃拉託斯特尼(Eratosthenes)篩法

問題:多個輸入,問關於素數相關的問題。 若是用上述方法確定爆。多組輸入的最好解決辦法是打表。至於打表,若是上述的打表nlogn打表的話會TLE,因此就要換一種思考方式。

埃氏篩的核心思想就是==將素數的倍數肯定爲合數==。對於一個從2開始連續數據,咱們假設其中每個都是素數。若是一個數爲素數,那麼這個數的倍數必定也是素數。因此算法大體流程: 2: [i=(2+2)—>(+2)數組尾],4,6,8,10 * * 不是素數 3: [i=(3+3)—>(+3)數組尾],6,9,12 * * 不是素數 4: [i=4]不是素數,跳過 5: . . . . . . . . . . . . 這個到除了前面幾回的計算量多一點,到後面隨着數據增大對整個複雜度相加是比較小的,算法複雜度爲O(nloglogn);別小瞧多的這個logn,數據量大一個log可能少很多個0,那時間也是十倍百倍甚至更多的差距。 具體代碼爲

static boolean isprime[];
static long prime[];
static void getprime() {
		prime=new long[100001];//記錄第幾個prime
	    int index=0;
		isprime=new boolean [1000001];
		for(int i=2;i<1000001;i++)
		{
			if(!isprime[i])
			{
				prime[index++]=i;
			}
			for(int j=i+i;j<1000000;j=j+i)//他的全部倍數都over
			{
				isprime[j]=true;					
			}
		}
	}
複製代碼

歐拉篩——線性篩

對於上述的埃氏篩,雖然能解決大部分的問題。可是不難發現裏面仍是有挺多不夠精簡的地方,好比有的地方會重複訪問。而歐拉篩在埃氏篩的基礎上進行優化,達到O(n)的複雜度。

static boolean isprime[];
static int prime[];
static void getprimeoula()// 歐拉篩 {
		prime = new int[100001];// 記錄第幾個prime
		int index = 0;
		isprime = new boolean[1000001];
		for (int i = 2; i < 1000001; i++) {
			if (!isprime[i]) {
				prime[index++] = i;
			}
			for (int j = 0; j < index && i * prime[j] <= 100000; j++){
				isprime[i * prime[j]] = true;// 
				if (i % prime[j] == 0)
					break;
			}
		}

	}
複製代碼

惟一分解定理

算數分解定理(惟一分解定理):

算數分解定理(惟一分解定理):

定義:

任何一個大於1的天然數 N,若是N不爲質數,那麼N能夠惟一分解成有限個質數的乘積 N=P1a1 P2a2P3a3…Pnan,這裏P1<P2<P3…<Pn均爲質數,其中指數ai是正整數。這樣的分解稱爲 N 的標準分解式.

應用:對於一個正整數n,若是n=q1a1 * q2 a2 * …* qnan,那麼他的正因數個數爲(1+a1) * (1+a2)* . . . *(1+an);

樣例:

1000=2* 500=2* (2 * 250)=2 * 2 *( 2 * 125)=2 * 2 * 2 *(5 * 25)=2 * 2 * 2 * 5 * (5 *5)=23*53.能夠看的出來基本策略就是從2開始除,直到不是2的倍數而後網上遞增求。

計算方法:

計算n的分解方式。主要是經過數的自身對從最小的質數開始整除除一直到不能整除,直到跳出限制條件。

  1. 你能夠從2到n;逐個遍歷判斷,知足條件的話就在數組中添加對應的count。固然,每被計算一次的時候,這個數就要被除一次。

  2. 上面方法對於大的數據顯然複雜度過高。其實你能夠從2遍歷到根號n+1(i * i<n+1)中止,由於最大的理想乘積是i*i=n,其餘的數據都是一個左,一個右面。最終分解的時候若是都分到這步了說明要麼後面不剩,要麼就是剩一個大質數

  3. 上面雖然從數的量級減小了很多,可是會遍歷不少沒用的合數,好比遍歷過2全部而的倍數都不須要遍歷判斷,因此咱們只須要遍歷素數。素數打表遍歷素數,當遇到多組輸入,數據要求較高的時候,先用素數篩打表,把素數預處理,而後直接從2-素數數組中遍歷便可,由於若是一個數能被拆,那麼他若是不能被拆,他就是素數,若是它還能被拆,那麼它就是合數。因此一個數被分解到最後都是素數的次冪相乘!很重要!這樣可以省的更多的時間。能夠參考素數篩模板

核心代碼:
不打表:

for(int j=2;j*j<n+1;j++)
{
		while(n%j==0)
		{
		     n/=j;
		      //根據需求操做
		}
		//根據需求操做...
	}
複製代碼

打表:

static int index=0;//根據數據範圍範圍內素數的最後一個位置prime[index]
long prime[]=new long[100001];
boolean isprime[]=new boolean[1000001];
for(int i=2;i<1000001;i++)//埃拉托色尼篩選法
{
	if(!isprime[i])
	{
		prime[index++]=i;
		for(int j=i+i;j<1000000;j=j+i)
		{
			isprime[j]=true;					
		}
	}
}
........
........
    // int n;
	for(int j=0;j<index;j++)
	{
			if(n==0||prime[j]>n) {break;}//素數已經超出沒法最大的數,退出
			long team=0;//其餘操做,能夠是你本身的邏輯
		    while(c%prime[j]==0)
		    {
		    	c/=prime[j];
		    	team++;//其餘操做
		    }
		    number*=(1+team);//其餘操做
		}	
複製代碼

惟一分解定義有什麼用?

例如給一個1000,這樣的數,問有多少種可能組合使得a * b=1000.或者求a*b中在那個範圍內a,b的排列次數。

首先要了解一個惟一分解定理的應用:對於一個正整數n,若是n=q1a1 * q2 a2 * …* qnan,那麼他的正因數個數爲(1+a1) * (1+a2)(1+an);對於這個,就能夠衍生其餘問題,好比找兩個因數的組合狀況,可能性,在那個範圍等等。其實這就是一個組合問題。對於每個數有t個,可以影響最終結果的就是這個素數出現的次數。若是細看雖然每一個數的機率都是可能出現和不出現的1/2.可是對於最終結果就是:出現0次,出現1次,出現。。。,出現t次。因此這個數對結果出現的可能行變成了原來次數*(1+t).以此類推,即可獲得全部的因數可能的結果。

就例如1000=23 * 53:
對於結果首先2和5是獨立互不影響的。因此對於一個因數。質數2的搭配有四種,出現0個,1個,2個或3個。同理質數5的搭配也是4種,因此最終因數可能出現的次數是4 * 4=1*(3+1)*(3+1)=16個。

### 例題 看下[hdu5248](http://acm.hdu.edu.cn/showproblem.php?pid=5428)

題意:給出n個數,求這n個數中最小的兩個因數乘積,題意有些小的歧義不太好理解。說明白了就是讓你從n個數分解找最小的兩個因子相乘.(1不知足由於1沒有2個及以上因子).

思路:數據量不大,能夠不打表直接素數分解。其實每一個數找到2個因子就能夠中止了,放到list或者數組中,最後排序判斷因子是否大於等於2個。按照要求輸出

附上ac代碼:

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class hdu5248 {

	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		int T=sc.nextInt();
		for(int q=0;q<T;q++)
		{
			int n=sc.nextInt();
			List<Long>list=new ArrayList<Long>();
			for(int i=0;i<n;i++)
			{
				long te=sc.nextLong();
				int num=0;
				for(int j=2;(long)j*j<te+1;j++)
				{
					while(te%j==0)
					{
						list.add((long) j);
						te/=j;
						num++;
					}
					if(num>2)break;//其實找到兩個就不用找了
				}
				if(te>1) {list.add(te);}
			}
			if(list.size()<2)
				System.out.println(-1);
			else {
				list.sort(null);
				System.out.println((long)(list.get(0)*list.get(1)));
			}
		}

	}
}
複製代碼

lightoj1341 題目大意:給個面積s。一個邊b,問最小邊大於b的全部可能狀況。 思路:總體-多餘。先求出全部的排列次數,而後除以二(要求組合隊數)。再從0頭到b開始剪掉多餘的狀況。不須要考慮特大的那邊,由於是對稱的已經除以過二了。 ac代碼:

埃氏篩

import java.util.Scanner;
public class testC {
static int index=0;
public static void main(String[] args) {
	// TODO 自動生成的方法存根
	long prime[]=new long[100001];
	boolean isprime[]=new boolean[1000001];
	for(int i=2;i<1000001;i++)//埃拉托色尼篩選法
	{
		if(!isprime[i])
		{
			prime[index++]=i;
			for(int j=i+i;j<1000000;j=j+i)
			{
				isprime[j]=true;					
			}
		}
	}
	Scanner sc=new Scanner(System.in);
	int t=sc.nextInt();
	for(int i=0;i<t;i++)
	{
		long a=sc.nextLong();
		long b=sc.nextLong();
		long number=1;
		long c=a;
		if(b*b>=a) {number=0;}//不可能的狀況,最小邊大於可能拼成的狀況
		else {
		for(int j=0;j<index;j++)
		{
			if(c==0||prime[j]>c) {break;}//超出界,跳出
			long team=0;
		    while(c%prime[j]==0)
		    {
		    	c/=prime[j];team++;
		    }
		    number*=(1+team);//計算
		}	
		if(c>1)number*=2;
		number/=2;
		//這裏別被繞進去。這裏不是按照次冪計算的,而是按照實打實的一個一個數判斷的。
		for(int j=1;j<b;j++)
		{
			if(a%j==0)number--;
		}
		}
		System.out.println("Case "+(i+1)+": "+number);
	}
}

}
複製代碼

歐拉篩:

import java.util.Scanner;

public class testC {
  static int index=0;
	public static void main(String[] args) {
		// TODO 自動生成的方法存根
		int prime[] = new int[100001];// 記錄第幾個prime
		int index = 0;
		boolean isprime[] = new boolean[1000001];
		for (int i = 2; i < 1000001; i++) {
			if (!isprime[i]) {
				prime[index++] = i;
			}
			for (int j = 0; j < index && i * prime[j] < 1000001; j++) {
				isprime[i * prime[j]] = true;// 找到的素數的倍數不訪問
				if (i % prime[j] == 0)
					break;// 關鍵!!!!
			}
		}
		Scanner sc=new Scanner(System.in);
		int t=sc.nextInt();
		for(int i=0;i<t;i++)
		{
			long a=sc.nextLong();
			long b=sc.nextLong();
			long number=1;
			long c=a;
			if(b*b>=a) {number=0;}//不可能的狀況,最小邊大於可能拼成的狀況
			else {
			for(int j=0;j<index;j++)
			{
				if(c==0||prime[j]>c) {break;}
				long team=0;
			    while(c%prime[j]==0)
			    {
			    	c/=prime[j];team++;
			    }
			    number*=(1+team);
			}	
			if(c>1)number*=2;
			number/=2;
			for(int j=1;j<b;j++)
			{
				if(a%j==0)number--;
			}
			}
			System.out.println("Case "+(i+1)+": "+number);
		}
	}
}
複製代碼

歐拉函數

求歐拉函數代碼的方式:直接求和打表 歐拉函數的做用:一個數n,求小於n的互質的個數。特例:1——oula(1)=1; 歐拉函數的公式:

在這裏插入圖片描述
其中i爲x全部質因數。 注意:每種質因數只一個 爲何會這樣?首先,互質的兩個數必定不能有公共因數。好比9和7互質,9和12不互質,由於有共同因數3.

那麼我難道須要一個個循環比較嗎?

答案先然不可能,由於若是數值過大這是個很大的複雜度。那麼我該如何處理?

換一種思惟。好比求24中的互質個數。答案是1,5,7,11,13,17,19,23。共8個

  1. 24=2 * 2 * 2 * 3;那麼在小於12中的數的核心共同質數爲2的倍數或者三的倍數。有人可能說明明還要4,6的倍數,那是由於這些倍數囊括在2,3之中。因此咱們每一個質因數只記錄一個
  • 看在24中,有1/2的是2的倍數,也就是1/2的數是和24有共同因數2.那麼就有(1-1/2)的數和24沒有共同因數2;
  • 同理那麼就有1/3的數和24有共同因數3,而且(1-1/3)=2/3的數沒有共同因數3.那麼沒有因數2和因數三剩下的不就是和24互質麼,那麼機率=(1-1/2) *乘以 (1-1/3)=1/3.總個數爲24 *乘以 1/3=8知足要求。
  • 2. 若是一個因數出現屢次怎麼排除呢。或者怎麼防止4,6這些數被計入因數中,這就要用到**質因數分解的思想**。只不過咱們不須要這個冪出現的次數,只須要讓剩餘的不可能在存在當前這個數爲因數的可能性。

    附上直接求代碼:

    private static int oula(int team) {
    		int i=0;int res=team;int team1=team;
    		for(i=2;i<(int)Math.sqrt(team1)+1;i++)
    		{
    			if(team%i==0) {
    				res=res/i*(i-1);
    				while(team%i==0) {team/=i;}//保證沒有i做爲因子 
    			}
    		}
    		if(team>1)res=res/team*(team-1);//說明後面還有一個比較大的互質因數大小爲team
    		return res;
    	}
    複製代碼

    其中,res爲結果,team1做爲求因數用。若是實在不能理解,好好看下質因數分解。

    打表代碼:

    int a[]=new int[1000001];
           for(int i=1;i<1000001;i++)
           {
        	   a[i]=i;
           }
           for(int i=2;i+2<1000001;i+=2)
           {
        	   a[i]/=2;
           }
           for(int i=3;i+2<1000001;i+=2)
           {
        	   if(a[i]==i)
        	   {
        		   for(int j=i;j+i<=1000001;j+=i)
        		   {
        			   a[j]=a[j]/i*(i-1);
        		   }
        	   }
           }
    複製代碼

    例題

    歐拉函數/素數斷定

    題目連接

    題目

    Bamboo Pole-vault是Xzhiland的一項大受歡迎的運動。 Phi-shoe大師是他成功的很是受歡迎的教練。他須要爲他的學生提供一些竹子,因此他讓他的助手Bi-Shoe去市場購買。市場上有不少可能的整數長度的Bamboos(是的!)。根據Xzhila的傳統,

    竹子的分數=Φ(竹子的長度)

    (Xzhilans很是喜歡數論)。對於您的信息,Φ(n)=小於n的數字,它們相對於素數(除了1以外沒有公約數)到n。所以,長度爲9的竹子的得分爲6,由於1,2,4,5,7,8是9的相對素數。

    助理雙鞋必須爲每一個學生買一個竹子。做爲一個扭曲,Phi-shoe的每一個撐杆跳學生都有一個幸運數字。 Bi-shoe但願購買竹子,這樣他們每一個人都會獲得一張分數大於或等於他/她的幸運數字的竹子。 Bi-shoe但願最大限度地減小購買竹子所花費的總金額。一個竹子單位花費1 Xukha。幫助他

    輸入 輸入以整數T(≤100)開始,表示測試用例的數量。

    每一個案例都以包含整數n(1≤n≤10000)的行開頭,表示Phi-shoe的學生人數。下一行包含n個空格分隔的整數,表示學生的幸運數字。每一個幸運數字將位於[1,106]範圍內。

    輸出 對於每種狀況,打印案例編號和購買竹子所花費的最少金額。有關詳細信息,請參閱示例 Sample Input 3 5 1 2 3 4 5 6 10 11 12 13 14 15 2 1 1 Sample Output Case 1: 22 Xukha Case 2: 88 Xukha Case 3: 4 Xukha

    題意

    題意:給你n個整數,第i個整數爲Xi。定義phi(k)爲k的歐拉函數值,設pi爲知足phi(pi)>=Xi的最小整數,題目就是要求sum(p1,p2,p3,...,pn)。

    告訴你幸運數字x,你找出phi(n)=x的這個最小的n,若干個這樣數的合。

    首先要清楚幾個概念phi(n)=n-1,==n爲素數==時候。由於n和小於它的任意都互質。 因此解題思路大體有兩個:

    歐拉函數的角度:

    歐拉是最明顯的,要找出大於這個數最小的那個phi[i],若是==單個歐拉函數求會TL==因此須要歐拉打表。沒輸入一個數網上找幾個就好了

    素數角度

    n爲素數時候,phi(n)=n-1,因此第一個phi(i)=t的那個i就是在t右側的第一個素數。有了這個思路你就能夠用素數解決問題,能夠用素數篩。用直接的素數斷定也能過。 c++代碼: 直接斷定

    #include <iostream>
    #include<stdio.h>
    using namespace std;
    #define ll long long
    bool isprime(int index) {
    		if(index<=2)return true;
    		else {
    			for(int i=2;i*i<index+1;i++)
    			{
    				if(index%i==0)return false;
    			}
    			return true;
    		}
     }
    int main() {
    
           int t;cin>>t;
           for(int i=0;i<t;i++)
           {
        	   int n;cin>>n;ll count=0;
        	   for(int j=0;j<n;j++)
        	   {
        		   ll team;
        		   cin>>team;
        		   int index=team+1;
        		   while(!isprime(index))
                    {index++;}
        		   count+=index;
    
        	   }
        	   //string s=" Xukha";
        	    printf("Case %d: %lld Xukha\n",(i+1),count);
           }
           return 0;
    
    }
    複製代碼

    歐拉篩

    #include <iostream>
    #include<stdio.h>
    using namespace std;
    #define ll long long
    const int MAXN=1100000+7;
    int m;
    ll a[MAXN],euler[MAXN];
    void phi() {
        for(int i=1;i<=m;i++)
        a[i]=i;
        for(int i=2;i<=m;i+=2)
        a[i]>>=1;
        for(int i=3;i<=m;i++)
        {
            if(a[i]==i)
            {
                for(int j=i;j<=m;j+=i)
                a[j]=(a[j]/i)*(i-1);
            }
        }
    }
    int main() {
             m=1100000;
             phi();
           int t;cin>>t;
    
           for(int i=0;i<t;i++)
           {
               ll n,count;
        	   cin>>n;count=0;
        	   for(int j=0;j<n;j++)
        	   {
        		   ll team;
        		   cin>>team;
        		   int index=team+1;
        		   while(a[index]<team)
                    {index++;}
        		   count+=index;
        	   }
        	    printf("Case %d: %lld Xukha\n",(i+1),count);
           }
           return 0;
    
    }
    複製代碼

    java 歐拉打表(能夠本身改爲素數)

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.io.PrintWriter;
    import java.io.StreamTokenizer;
    import java.util.Scanner;
    public class Main{
    	public static void main(String[] args) throws IOException {
    		// TODO 自動生成的方法存根
    		StreamTokenizer in=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    		PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
           in.nextToken();int t=(int)in.nval;
           int a[]=new int[1100001];
           for(int i=1;i<1100001;i++)
           {
        	   a[i]=i;
           }
           for(int i=2;i+2<1100001;i+=2)
           {
        	   a[i]/=2;
           }
           for(int i=3;i+2<1100001;i+=2)
           {
        	   if(a[i]==i)
        	   {
        		   for(int j=i;j+i<=1100001;j+=i)
        		   {
        			   a[j]=a[j]/i*(i-1);
        		   }
        	   }
           }
      
           for(int i=0;i<t;i++)
           {
        	   in.nextToken();int n=(int)in.nval;long count=0;
        	   for(int j=0;j<n;j++)
        	   {
        		   in.nextToken();
        		   long team=(long)in.nval;
        		   int index=(int) (team+1);
        		   while(a[index]<team) {index++;}
        		   count+=index;
        		   //System.out.println(oula(team));
        	   }
        	   System.out.println("Case "+(i+1)+": "+count+" Xukha");
           }
    	}
    
    	private static boolean isprime(int index) {
    		if(index<=2)return true;
    		else {
    			for(int i=2;i*i<index+1;i++)
    			{
    				if(index%i==0)return false;
    			}
    			return true;
    		}
    		
    	}
    
    	private static int oula(int team) {
    		int i=0;int res=team;int team1=team;
    		for(i=2;i<(int)Math.sqrt(team1)+1;i++)
    		{
    			if(team%i==0) {
    				res=res/i*(i-1);
    				while(team%i==0) {team/=i;}//保證I是素數 
    			}
    		}
    		if(team>1)res=res/team*(team-1);
    		return res;
    	}
    }
    複製代碼

    在這裏插入圖片描述

    github一些刷題代碼

    我的公衆號:bigsai

    相關文章
    相關標籤/搜索