Acdreamer博客數論學習(1)

Acdreamer博客數論學習

Day One

等比數列求和.

求解Sn=(a+a2+...+an)modM

採用二分.n%2==0時 Sn=(1+a(n2))Sn2 ,n%2==1時,Sn=(1+an+12)Sn12+an+12

#include<cstdio>
#include<iostream>
#define ll long long
using namespace std;

const int mod = 7;

ll pow(int a, int k){
    ll ans = 1;
    while(k){
        if(k&1)ans=ans*a%mod;
        a=a*a%mod;
        k>>=1;
    }
    return ans;
}

ll S(int a, int n){
    if(n==1)return a%mod;
    if(n&1){
        return ((1+pow(a,(n+1)/2))%mod*S(a,(n-1)/2)%mod + pow(a,(n+1)/2)%mod)%mod;
    }else{
        return (1+pow(a,n/2))%mod*S(a,n/2)%mod;
    }
}

int main(){
    int a,n;
    while(~scanf("%d%d",&a,&n)){
        printf("%lld\n",S(a,n));
    }
    return 0;
}

題目:https://vjudge.net/problem/POJ-3233

計算矩陣1-n次方和

int n,k,m;
int mod = 1e9+7;
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;

struct Matrix{
    int mp[35][35];
    Matrix(){
        memset(mp,0,sizeof mp);
    }
    Matrix(int v){
        memset(mp,0,sizeof mp);
        for(int i = 1; i <= n; i++){
            mp[i][i] = v;
        }
    }
};

Matrix multi(Matrix a, Matrix b){
    Matrix c;
    for(int i = 1; i <= n; i++){
        for(int k = 1; k <= n; k++){if(a.mp[i][k])
            for(int j = 1; j <= n; j++){if(b.mp[k][j])
                c.mp[i][j] = c.mp[i][j] + a.mp[i][k]*b.mp[k][j];
                c.mp[i][j] %= mod;
            }
        }
    }
    return c;
}


Matrix pls(Matrix a, Matrix b){
    Matrix c;
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= n; j++){
            c.mp[i][j] = a.mp[i][j]+b.mp[i][j];
            c.mp[i][j] %= mod;
        }
    }
    return c;
}

Matrix powmul(Matrix a, int k){
    Matrix c = Matrix(1);
    for(int i = 1; i <= n; i++)
        c.mp[i][i] = 1;
    while(k){
        if(k&1)c = multi(c,a);
        a = multi(a,a);
        k>>=1;
    }
    return c;
}



Matrix S(Matrix a, int k){
    Matrix temp = Matrix(1);
    if(k==1)return a;
    if(k&1){
        Matrix temp2 = powmul(a,(k+1)/2);
        return pls(multi(pls(temp, temp2), S(a,(k-1)/2)), temp2);
    }else{
        Matrix temp2 = powmul(a,k/2);
        return multi(pls(temp, temp2), S(a,k/2));
    }
}

int main(){
    while(~scanf("%d%d%d",&n,&k,&m)){
        Matrix a;
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j++){
                scanf("%d",&a.mp[i][j]);
            }
        }
        mod = m;
        a = S(a,k);
        for(int i = 1 ; i <= n; i++){
            for(int j = 1; j <= n; j++){
                printf("%d",a.mp[i][j]);
                if(j!=n)printf(" ");
            }
            puts("");
        }
    }
    return 0;
}

素數定理

π(n)nlimnπ(n)n/lnn=1

定理gcd(an1,am1)=agcd(n,m)1

在a>1,m,n>0時成立.

#include<cstdio>
#include<iostream>
#define ll long long
using namespace std;
int mod = 1e9+7;
ll gcd(ll a, ll b){
    return b?gcd(b,a%b):a;
}

ll pow(ll a, ll k){
    ll ans = 1;
    while(k){
        if(k&1)ans=ans*a%mod;
        a = a*a%mod;
        k>>=1;
    }
    return ans;
}

int main(){
    int a,m,n,k1,t;scanf("%d",&t);
    while(t--){
        scanf("%d%d%d%d",&a,&m,&n,&k1);
        mod = k1;
        ll ans = pow(a,gcd(n,m))-1+mod;
        printf("%lld\n",ans%mod);
    }
    return 0;
}

定理擴展a>b,gcd(a,b)=1,gcd(ambm,anbn)=agcd(m,n)bgcd(m,n)

定理G=gcd(C1n,C2n,C3n,...,Cnn1)

  1. n爲素數,答案爲n;
  2. n有多個素因子,答案爲1;
  3. n只有一個素因子,答案就是該素因子(其實包括了第一種情況

題目:https://vjudge.net/problem/HDU-2582

#include<cstdio>
#include<iostream>
#include<cstring>
#define ll long long
#define maxn 1000100
using namespace std;

bool judge[maxn];
int prime[maxn/10];
ll biao[maxn];
int tot = 0;
void init(){
    memset(prime,0,sizeof prime);
    memset(judge,0,sizeof judge);
    for(int i = 2; i < maxn; i++){
        if(!judge[i]){
            for(int j = i+i; j < maxn; j+=i){
                judge[j]=1;
            }
            prime[tot++]=i;
        }
    }
    fill(biao,biao+maxn,1);
    for(int i = 0; i < tot; i++){
        for(ll j = prime[i]; j < maxn; j*=prime[i]){
            biao[j]=prime[i];
        }
    }
    for(int i = 4; i < maxn; i++){
        biao[i]+=biao[i-1];
    }
}

int main(){
    int n;
    init();
    while(~scanf("%d",&n)){
        printf("%lld\n",biao[n]);
    }
    return 0;
}

定理Fngcd(Fm,Fn)=Fgcd(m,n)

http://acm.nyist.net/JudgeOnline/problem.php?pid=468

此處需要學會一種新的判斷素數的方式叫做Miller_Rabin素數測試.在DayTwo中有.

#include<stdio.h>
#include<algorithm>
#include<iostream>
using namespace std;

long long multi(long long a, long long b, long long mod){
    long long temp = a, sum = 0;
    while(b){
        if(b & 1) sum = (sum + temp) % mod;
        temp = (temp + temp) % mod;
        b = b >> 1;
    }
    return sum;
}

long long Modular_exponent(long long a, long long x, long long mod){
    long long t = a % mod, r = 1;
    while(x){
        if(x & 1) r = multi(r, t, mod);
        t = multi(t, t, mod); x = x >> 1;
    }
    return r;
}

bool Miller_Rabin(long long n){
    long long time = 20;
    if(n < 2) return false;
    if(n == 2) return true;
    bool flag = false;
    for(long long k = 0; k <= time; ++k){
        flag = false;
        long long d = n - 1, r = 0, t, a = rand() % (n - 2) + 2;
        while((d & 1) == 0){
            d = d>>1;
            r++;
        }
        t = Modular_exponent(a, d, n);
        if(t == 1 || t == n-1) {flag = true; continue;}
        for(long long i = 1; i < r; i++){
            t = multi(t, t, n);
            if(t == 1) {flag = false; return flag;}
            if(t == n-1) {flag = true; break;}
        }
        if(!flag) break;
    }
    return flag;
}

int main()
{
    long long n;
    while(scanf("%lld",&n)!=EOF)
    puts((n == 4 || (n != 2 && Miller_Rabin(n))) ? "Yes" : "No");
    return 0;
} 

定理:給定兩個互素的正整數A和B,那麼他們最大不能組合的數爲AB-A-B,不能組合的個數爲num=(A1)(B1)2

ax+by=c的存在非負解的條件就是c>AB-A-B.

HDU - 1792

#include<cstdio>
#include<iostream>
using namespace std;
int main(){
    int a,b;
    while(~scanf("%d%d",&a,&b)){
        printf("%d %d\n",a*b-a-b,(a-1)*(b-1)/2);
    }
    return 0;
}

定理gcd(i,N)=dϕ(Nd)

數論專題裏也有一題如是說.

https://vjudge.net/problem/POJ-2480

#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long
#define maxn 50000
using namespace std;
bool judge[maxn];
int prime[maxn];
int tot;
void init(){
    tot = 0;
    memset(judge,0,sizeof judge);
    memset(prime, 0, sizeof prime);
    for(int i = 2; i < maxn; i++){
        if(!judge[i]){
            for(int j = i+i;  j < maxn; j+=i){
                judge[j] = 1;
            }
            prime[tot++]=i;
        }
    }
}

int main(){
    init();
    ll n;
    while(~scanf("%lld",&n)){
        ll ans = n;
        for(int i = 0; i < tot && prime[i]*prime[i] <= n; i++){
            if(n%prime[i]==0){
                int cot = 0;
                while(n%prime[i]==0){cot++;n/=prime[i];}
                ans += ans * cot * (prime[i]-1)/prime[i];
            }
        }
        if(n!=1)ans += ans*(n-1)/n;
        printf("%lld\n",ans);
    }
}

剩下沒有題目的定理

(n+1)lcm(C0n,C1n,C2n,...,Cn1N,CNn)=lcm(1,2,...,n+1)

nn!

結論:

pC1p,C2p,C3p,...Cp1p,pp(x+y+..+w)pxp+yp+...+wp(modp)

Day Two

盧卡斯-萊默檢驗法 檢驗梅森素數

Mp=2p1p{si}i0,si={4,i=0s2i12,i>0Mpsp20(modMp)
#include<cstdio>
#include<iostream>
#define ll long long
using namespace std;

ll T,p;
ll multi(ll a, ll b, ll m){
    ll ans = 0;
    while(b){
        if(b&1)ans = (ans+a)%m;
        a = (a+a)%m;
        b>>=1;
    }
    return ans;
}

int main(){
    scanf("%lld",&T);
    while(T--){
        scanf("%lld",&p);
        ll n = ((ll) 1<<p)-1;
        ll r = 4;
        if(p==2)puts("yes");
        else{
            while((--p)!=1)r=(multi(r,r,n)-2+n)%n;
            if(r%n==0)puts("yes");
            else puts("no");
        }
    }
}

Miller-Rabin素數測試

費馬小定理:對於素數p和任意整數a,有

apa(modp)
(同餘)。反過來,滿足
apa(modp)
,p也幾乎一定是素數。

  僞素數:如果n是一個正整數,如果存在和n互素的正整數a滿足 an11(modn) ,我們說n是基於a的僞素數。如果一個數是僞素數,那麼它幾乎肯定是素數。

​ 序列:
​ a^m%n; a^(2m)%n; a^(4m)%n; …… ;a^(m*2^q)%n把上述測試序列叫做Miller測試,關於Miller測試,有下面的定理:

定理:若n是素數,a是小於n的正整數,則n對以a爲基的Miller測試,結果爲真.Miller測試進行k次,將合數當成素數處理的錯誤概率最多不會超過4^(-k).

  Miller-Rabin測試:不斷選取不超過n-1的基b(s次),計算是否每次都有

bn11(modn)
,若每次都成立則n是素數,否則爲合數。 

二次探測定理 :p爲素數,0

#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<iostream>
#define ll long long
using namespace std;
const int Times = 10;

ll multi(ll a, ll b ,ll m){
    ll ans = 0;
    a%=m;
    while(b){
        if(b&1)ans = (ans+a)%m;
        a = (a+a)%m;
        b >>= 1;
    }
    return ans;
}

ll quick_mod(ll a, ll b, ll m){
    ll ans = 1;
    a %= m;
    while(b){
        if(b&1)ans = multi(ans, a, m);
        a = multi(a,a,m);
        b>>=1;
    }
    return ans;
}

bool Miller_Rabin(ll n){
    if(n==2)return true;
    if((n<2) || !(n&1))return false;
    ll m = n-1;
    int k = 0;
    while((m&1)==0){
        k++;
        m>>=1;
    }
    for(int i = 0; i < Times; i++){
        ll a = rand()%(n-1)+1;
        ll x = quick_mod(a,m,n);
        ll y = 0;
        for(int j = 0; j < k; j++){
            y = multi(x,x,n);
            if(y==1 && x!=1 && x!= n-1)return false;
            x = y;
        }
        if(y!=1)return false;
    }
    return true;
}

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        ll n;
        scanf("%lld",&n);
        if(Miller_Rabin(n))puts("Yes");
        else puts("No");
    }
    return 0;
}

例題:[http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1186

java寫大數:

import java.math.BigInteger;
import java.util.Random;
import java.util.Scanner;

/**
 * Created by huchi on 17-7-18.
 */

public class test {
    public static final int Times = 10;

    public static BigInteger quick_mod(BigInteger a, BigInteger b, BigInteger m){
        BigInteger ans = BigInteger.ONE;
        a = a.mod(m);
        while(!b.equals(BigInteger.ZERO)){
            if(b.mod(BigInteger.valueOf(2)).equals(BigInteger.ONE)){
                ans = (ans.multiply(a)).mod(m);
                b = b.subtract(BigInteger.ONE);
            }
            b = b.divide(BigInteger.valueOf(2));
            a = (a.multiply(a)).mod(m);
        }
        return ans;
    }

    public  static boolean Miller_Rabin(BigInteger n){
        if(n.equals(BigInteger.valueOf(2)))return true;
        if(n.equals(BigInteger.ONE))return false;
        if((n.mod(BigInteger.valueOf(2))).equals(BigInteger.ZERO))return false;
        BigInteger m = n.subtract(BigInteger.ONE);
        BigInteger y = BigInteger.ZERO;
        int k = 0;
        while((m.mod(BigInteger.valueOf(2))).equals(BigInteger.ZERO)){
            k++;
            m = m.divide(BigInteger.valueOf(2));
        }
        Random d = new Random();
        for(int i = 0; i < k; i++){
            int t = 0;
            //隨機數
            if(n.compareTo(BigInteger.valueOf(10000))==1){
                t = 10000;
            }else{
                t = n.intValue()-1;
            }
            int a = d.nextInt(t) + 1;
            BigInteger x = quick_mod(BigInteger.valueOf(a), m, n);
            //測試
            for(int j = 0; j < k; j++){
                y = (x.multiply(x)).mod(n);
                if(y.equals(BigInteger.ONE)
                        && !(x.equals(BigInteger.ONE))
                        && !(x.equals(n.subtract(BigInteger.ONE)))){
                    return false;
                }
                x = y;
            }
            if(!(y.equals(BigInteger.ONE)))return false;
        }
        return true;
    }

    public static void main(String[] args){
        Scanner in = new Scanner(System.in);
        while(in.hasNextBigInteger()){
            BigInteger n = in.nextBigInteger();
            if(Miller_Rabin(n))System.out.println("Yes");
            else System.out.println("No");
        }
    }
}

擴展歐幾里得

二元一次方程

ax1+by1=gcd(a,b)
;

a>b時,b==0時,x=1,y=0, 否則設bx2+(a mod b)y2=gcd(b,a mod b)=gcd(a,b)

又有ax1+by1=bx2+(a[ab]b)y2

則有x1=y2,y1=x2[ab]y2 , 用遞推實現.

void e_gcd(int a, int b, int &d, int &x, int &y){
    if(!b){
        d=a,x=1,y=0;
        return;
    }else{
        e_gcd(b,a%b,d,y,x);
        y -= (a/b)*x;
    }
}

代表的是ax+by=gcd(a,b)的解x,y,和d爲gcd(a,b);,得到的x,y中a>b則x爲正.在gcd(a,b)==1的時候,可以領y=(y%a+a)%a把y變成正的,再根據等式把x變成負的就行.

例題:https://vjudge.net/problem/POJ-2142

#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;

int gcd(int a, int b){
    return b?gcd(b,a%b):a;
}

void e_gcd(int a, int b, int &d, int &x, int &y){
    if(!b){
        d=a,x=1,y=0;
        return;
    }else{
        e_gcd(b,a%b,d,y,x);
        y -= (a/b)*x;
    }
}

int main(){
    int a,b,c;
    while(~scanf("%d%d%d",&a,&b,&c) && (a+b+c)){
        int x, y, d, tx, ty;
        int gd = gcd(a,b);
        a /= gd; b/= gd; c/= gd;
        e_gcd(a,b,d,x,y);
        //獲取x非負的時候的情況
        tx = x*c;
        tx = (tx%b + b)%b;
        ty = (c-tx*a)/b;
        if(ty < 0)ty = -ty;
        //獲取y非負的情況
        y = y*c;
        y = (y%a + a)%a;
        x = (c-b*y)/a;
        if(x<0)x =-x;
        if(x+y>tx+ty){
            x=tx;
            y=ty;
        }
        printf("%d %d\n",x,y);
    }
    return 0;
}

https://vjudge.net/problem/HIT-2815

題目沒讀清,特判就寫錯了,擴展歐幾里得和上面一道題一樣的用.

#include<cstdio>
#include<iostream>
#define ll long long
using namespace std;

ll gcd(ll a, ll b){
    return b?gcd(b, a%b):a;
}

void e_gcd(ll a, ll b, ll &d, ll &x, ll &y){
    if(!b){
        d = a; x = 1; y = 0;
        return ;
    }else{
        e_gcd(b,a%b,d,y,x);
        y -= (a/b)*x;
    }
}

int main(){
    ll t,a,b,d,x,y;scanf("%lld",&t);
    while(t--){
        scanf("%lld%lld",&a,&b);
        if(a*b==0){
            if(a==1 || b==1){
                puts("1");
            }else{
                puts("-1");
            }
        }else if(a==1 || b==1){
            if(max(a,b)==2)
                puts("1");
            else puts("2");
        }
        else if(gcd(a,b)!=1){
            puts("-1");
        }else{
            e_gcd(a,b,d,x,y);
            ll tx,ty;
            tx = (x%b+b)%b;
            ty = (1-a*x)/b;
            if(ty<0)ty = -ty;
            y = (y%a+a)%a;
            x = (1-b*y)/a;
            if(x<0)x=-x;
            if(x+y>tx+ty){
                x = tx;
                y = ty;
            }
            printf("%lld\n",x+y-1);
        }
    }
    return 0;
}

畢達哥拉斯三元組的解

x2+y2=z2 ,只考慮本原解.不妨假設x爲偶數,y,z爲奇數.這樣就必有x=2mn,y=m2n2,z=m2+n2

例題:https://vjudge.net/problem/POJ-1305

輸出兩個數據,一個是z不大於n的本原解的個數,和本原解中沒有用到的數的個數.

#include<cstdio>
#include<cstring>
#include<iostream>
#define maxn 1000111
using namespace std;

int biao[maxn];
bool flag[maxn];

int gcd(int a, int b){
    return b?gcd(b,a%b):a;
}

int main(){
    int n;
    memset(flag,0,sizeof flag);
    for(int i = 2; i*i < maxn; i++){
        for(int j = 1; j < i; j++){
            if(gcd(i,j)!=1)continue;
            if((i&1)&&(j&1))continue;
            if(i*i+j*j<maxn)biao[i*i+j*j]++;
        }
    }
    for(int i = 1; i < maxn; i++){
        biao[i]+=biao[i-1];
    }
    while(~scanf("%d",&n)){
    memset(flag,0,sizeof flag);
        int ans = 0;
        for(int i = 2; i*i < maxn; i++){
            for(int j = 1; j < i; j++){
                if(gcd(i,j)!=1)continue;
                if((i&1)&&(j&1))continue;
                for(int k = 1; k*(i*i+j*j)<=n; k++){
                    flag[2*i*j*k]=flag[k*(i*i-j*j)]=flag[k*(i*i+j*j)]=1;
                }
            }
        }
        for(int i = 1; i <= n; i++)
            if(flag[i])ans++;
        printf("%d %d\n",biao[n],n-ans);
    }
    return 0;
}

例題二:

利用畢達哥拉斯三元組+歐拉函數+容斥原理

https://vjudge.net/problem/HDU-3939???

尋找滿足的i,j滿足gcd(i,j)=1,dfs()容斥原理來找有多少互斥的數.

這裏需要判斷i<=j和i>j的情況.其中,i<=j時,i爲偶數,則直接加phi[i],i爲奇數時,由於j必須爲偶數,不能用phi[i],所以用容斥原理,一共i>>1個數(曬去了2的倍數).大於時同理.

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#define ll long long
#define maxn 1001000
using namespace std;

bool judge[maxn];
int prime[maxn/10];
int phi[maxn];
int check[40];
int tot,num;
ll ans;
void init(){
    tot = 0;
    memset(judge, 0, sizeof judge);
    for(int i = 2; i < maxn; i++){
        if(!judge[i]){
            for(int j = i+i; j < maxn; j+=i){
                judge[j]=1;
            }
            prime[tot++]=i;
        }
    }
    for(int i = 1; i < maxn; i++)phi[i]=i;
    for(int i = 2; i < maxn; i+=2)phi[i]>>=1;
    for(int i = 3; i < maxn; i+=2)if(phi[i]==i){
        for(int j = i; j < maxn; j+=i){
            phi[j] = phi[j] - phi[j]/i;
        }
    }
}
void prime_check(int n){
    num = 0;
    if(!judge[n]){
        check[num++]=n;
        return ;
    }
    for(int i = 0; i < tot && n > 1; i++){
        if(n%prime[i]==0){
            check[num++]=prime[i];
            while(n%prime[i]==0) n/=prime[i];
            if(n>1 && !judge[n]){
                check[num++]=n;
                return ;
            }
        }
    }
}
//容斥原理計算無關的數fun(j)=j-(j/c1+j/c2+j/c3)+(j/(c1*c2)+j/(c1*c3)+j/(c2*c3))-(j/(c1*c2*c3))。
void dfs(int k, int r, int s, int n){
    if(k==num){
        if(r&1) ans -= n/s;
        else ans+= n/s;
        return;
    }
    dfs(k+1, r, s, n);
    dfs(k+1, r+1, s*check[k], n);
}

ll L;
int main(){
    init();
    int t;scanf("%d",&t);
    while(t--){
        ans = 0;
        scanf("%lld",&L);
        int m = (int)sqrt(1.0*L);
        for(int i = m; i > 0; i--){
            int p = (int)sqrt(L - (ll)i*i);
            //需要尋找gcd(i,j)=1的個數
            if(i<=p){
                if(i&1){
                    prime_check(i);
                    dfs(0,0,1,i>>1);
                }else ans+=phi[i];
            }else{
                // i>p時,j所取的範圍在[1,p]
                prime_check(i);
                if(i&1)dfs(0,0,1,p>>1);
                else dfs(0,0,1,p);
            }
        }
        printf("%lld\n",ans);
    }
}

歐拉函數與歐拉定理

定理一:

mnϕ(mn)=ϕ(m)ϕ(n)
, 所以n爲奇數時,我們有ϕ(2n)=ϕ(n)

定理二: p是素數,有ϕ(pa)=papa1 ,容斥原理.

定理三: n=pα11pα22...pαkkϕ(n)=n(11p1)(11p2)...(11pk)  定理1+3

定理四: d|nϕ(d)=n 莫比烏斯反演.

定理五: (a,m)=1,則xbaϕ(m)1axb(mod m)

定理六: n>2時,ϕ(n) 爲偶數

定理三求解歐拉函數

int phi(int n){
    int ans = n;
    for(int i = 2; i*i <= n; i++){
        if(n%i==0){
            ans = ans - ans/i;
            while(n%i==0) n/=i;
        }
    }
    if(n>1)ans = ans-ans/n;
    return ans;
}

容斥原理,篩法去減不滿足的.

#define maxn 100000
int p[maxn];
void phi_table(){
    memset(p,0,sizeof p);
    for(int i = 1; i < maxn; i++)p[i]=i;
    for(int i = 2; i < maxn; i+=2)p[i]>>=1;
    for(int i = 3; i < maxn; i+=2){
        if(p[i]==i){
            for(int j = i; j < maxn; j+=i)
                p[j]=p[j]-p[j]/i;
        }
    }
}

二次篩法

這道題有類似的在kuangbin的數論基礎專題裏有出現過.

例題:https://vjudge.net/problem/POJ-2689

注意1 2這個樣例...還有曬的時候不能把質數也曬了....

#include<cstdio>
#include<iostream>
#include<cstring>
#define maxn 50000
int prime[maxn];
bool judge2[21*maxn];
bool judge1[maxn];
using namespace std;
int tot=0;
void init(){
    memset(judge1,0,sizeof judge1);
    for(int i = 2; i<maxn; i++){
        if(!judge1[i]){
            prime[tot++] = i;
            for(int j = i+i; j < maxn; j+=i){
                judge1[j]=true;
            }
        }
    }
}

int main(){
    init();
    unsigned int u,v;
    while(~scanf("%d%d",&u,&v)){
        if(u==1)u=2;
        memset(judge2,0,sizeof judge2);
        for(int i = 0; i < tot; i++){
            if(prime[i]>v)break;
            unsigned int j=u/prime[i]*prime[i];
            if(u%prime[i]==0 && u!=prime[i])judge2[0]=1;
            while(j<=u)j+=prime[i];
            if(j==prime[i])j+=j;
            for(;j<=v;j+=prime[i]){
                judge2[j-u]=1;
            }
        }
        int st,stlen=maxn,ed,edlen=0;
        int cot=0,len=0;
        for(int i = 0; i <= v-u; i++){
            if(!judge2[i]){
                if(len &&len<stlen){
                    st = i-len;
                    stlen = len;
                }
                if(len && len>edlen){
                    ed = i-len;
                    edlen = len;
                }
                cot++;
                len = 0;
            }
            len ++ ;
        }
        if(cot!=2 && st==ed){
            ed = st+stlen;
        }
        if(cot>1)printf("%d,%d are closest, %d,%d are most distant.\n",u+st,u+st+stlen,u+ed,u+ed+edlen);
        else puts("There are no adjacent primes.");
    }
    return 0;
}

逆元

正整數a,m,如果有ax1(mod m) ,這個同於方程中x的最小正整數解叫做a模m的逆元.

逆元一般用擴展歐幾里得算法來求.m爲素數時,由費馬小定理得到逆元爲am2modm

逆元常見問題

已知b|a,求解ans=abmod m

常見解法:擴展歐幾里得.m是素數時,直接用費馬小定理.但他們都需要a與m互素.

除法取模求逆元的通用方法: ans=a mod(mb)b

ab=km+x(x<m),a=kbm+bx,a mod(bm)=bx,x=a mod(bm)b

例題:https://vjudge.net/problem/POJ-1845

AB 的所有因子和對9901取餘的結果.

考慮

A=pα11pα22...pαkk,AB=pα1B1pα2B2...pαkBk

所有的因子和:

sum=1ik0jαiBpji

可以用二分求等比數列和,也可以用等比數列公式(需要用逆元)直接求.分別寫在了Method_one和Method_two中.

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#define ll long long
#define maxn 10000
using namespace std;
ll mod =9901;
bool judge[maxn];
ll check[maxn][2];
int prime[maxn/2];
int tot,num;
ll a,b;
void init(){
    tot=0;
    memset(judge, 0, sizeof judge);
    for(int i = 2; i < maxn; i++){
        if(!judge[i]){
            for(int j = i+i; j < maxn; j+=i){
                judge[j]=1;
            }
            prime[tot++]=i;
        }
    }
}

ll multi(ll a, ll b, ll m){
    ll ans = 0;
    a %= m;
    while(b){
        if(b&1)ans = (ans+a)%m;
        a = (a+a)%m;
        b >>= 1;
    }
    return ans;
}

ll largequickmod(ll p, ll k, ll m){
    ll ans = 1;
    while(k){
        if(k&1)ans = multi(ans,p,m);
        p = multi(p,p,m);
        k>>=1;
    }
    return ans;
}

ll quickmod(ll p, ll k){
    ll ans=1;
    p%=mod;
    while(k){
        if(k&1)ans = ans*p%mod;
        p = (p*p)%mod;
        k>>=1;
    }
    return ans;
}

ll S(ll p, ll k){
    if(p==0)return 0;
    if(k==0)return 1;
    ll temp = quickmod(p,(k>>1))%mod;
    if(k&1){
        return S(p,k>>1)%mod*(1+temp*p%mod)%mod;
    }else{
        return (S(p,(k>>1)-1)%mod*(1+temp*p%mod)%mod+temp)%mod;
    }
}

void Method_one(){
    ll ans = 1;
    for(int i = 0; i < num; i++){
        ans = (ans * S(check[i][0], b*check[i][1]))%mod;
    }
    if(a==0)ans=0;
    printf("%lld\n",ans);
}

void Method_two(){
    ll ans = 1;
    for(int i = 0; i < num; i++){
        ll M = mod*(check[i][0]-1);
        ans *= (largequickmod(check[i][0], check[i][1]*b+1, M)+M-1)/(check[i][0]-1);
        ans %= mod;
    }
    printf("%lld\n",ans);
}

int main(){
    init();
    while(~scanf("%lld%lld",&a,&b)){
        ll temp = a;
        num=0;
        memset(check, 0, sizeof check);
        for(int i = 0; i < tot; i++){
            if(temp<=1)break;
            if(temp%prime[i]==0){check[num++][0]=prime[i];}
            while(temp%prime[i]==0){
                temp/=prime[i];
                check[num-1][1]++;
            }
        }
        if(temp>1){
            check[num][0]=temp;
            check[num++][1]=1;
        }
        //Method_one();
        Method_two();
    }
    return 0;
}

1→p模p的所有逆元,這裏p爲奇質數.用快速冪的複雜度O(plog(p)),用遞推式可以優化到O(p):

inv[i]=(MM/i)inv[M%i]%Mt=M/i,k=M%i,ti+k0(modM),tik(modM)ik:tinv[k]inv[i](modM) inv[i]=(MM/i)inv[M%i]%M

初始化inv[1]=1,就可以通過遞推法求出1→p模奇素數的所有逆元.

http://www.lydsy.com/JudgeOnline/problem.php?id=2186

例題:1-N!中與M!互質的數的個數,M<=N;M!|N!, .當n是m的倍數時,1-n中與m互素的個數爲nmϕ(m)

題目中 N!M!ϕ(M!)=N!M!M!(11p1)(11p2)...(11pk) .  

時間卡的特別緊.bool開的就超時了.inv[i]算逆元.multi[maxn]算階乘取模,要求p與m互質,否則需要用除法取模.

#include<cstdio>
#include<bitset>
#define ll long long
#define maxn 10000005
using namespace std;
ll mod;
bitset<maxn> judge;
void init(){
    judge.set();
    for(int i = 4; i < maxn; i+=2)judge[i]=0;
    for(int i = 3; i < maxn; i+=2){
        if(judge[i]){
            for(int j = i+i; j < maxn; j+=i){
                judge[j]=0;
            }
        }
    }
}
ll multi[maxn];
int inv[maxn];
ll ans[maxn];

int main(){
    init();
    int T,R;
    scanf("%d%d",&T,&R);
    mod = R;
    multi[0]=1;
    for(int i = 1; i < maxn; i++){
        multi[i]=multi[i-1]*i%mod;
    }
    inv[1]=1;
    for(int i = 2; i < mod && i < maxn; i++){
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;
    }
    ans[1]=1;
    for(int i = 2; i < maxn; i++){
        if(judge[i]){
            ans[i] = ans[i-1]*(i-1)%mod;
            ans[i] = ans[i]*inv[i%mod]%mod;
        }else{
            ans[i] = ans[i-1];
        }
    }
    int n,m;
    while(T--){
        scanf("%d%d",&n,&m);
        ll anss = 1LL*multi[n]*ans[m]%mod;
        printf("%lld\n",anss);
    }
    return 0;
}

歐拉篩

void getphi()    
{    
   int i,j;    
   phi[1]=1;    
   for(i=2;i<=N;i++)//相當於分解質因式的逆過程    
   {    
       if(!mark[i])    
           {    
             prime[++tot]=i;//篩素數的時候首先會判斷i是否是素數。    
             phi[i]=i-1;//當 i 是素數時 phi[i]=i-1    
             }    
       for(j=1;j<=tot;j++)    
       {    
          if(i*prime[j]>N)  break;    
          mark[i*prime[j]]=1;//確定i*prime[j]不是素數    
          if(i%prime[j]==0)//接着我們會看prime[j]是否是i的約數    
          {    
             phi[i*prime[j]]=phi[i]*prime[j];break;    
          }    
          else  phi[i*prime[j]]=phi[i]*(prime[j]-1);//其實這裏prime[j]-1就是phi[prime[j]],利用了歐拉函數的積性    
       }    
   }    
}    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章