8.2数论(1)

素数

也有很多种方法

放上比较喜欢的两种,刷的题还不多,不知道哪个更优。。。

int sushu(int n){
    
    int k = sqrt(n);
    
    for(int i=2;i<=k;i++){
        if(n%i==0)
            return 0;
    }
   return 1;
}

 

bool prime[MAX];
int r[MAX];
 
void IsPrime()
{
    memset(prime,0,sizeof(prime));
    prime[0] = prime[1] = 1;
    for(int i = 2;i < MAX;i++)
      for(int j = 2;i*j < MAX;j++)
        prime[j*i] = 1;
}

上面这个不需要里面传入参数,同时用数组保存,注意 j 的初始值也从2开始

 

 线性筛法:  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

素数筛法
在这个基础上,如果我们将所有要判断的数放在一个容器里,每判断一个数,发现其为质数,那将它所有倍数筛去,即标记为1(容器初始化为0)。但这样又存在一个问题,例如发现3为质数,将它的15倍,即45筛去后,又发现5为质数,将他的9倍晒去,这样一来,45被重复操作,而数据较大时,会有大量的数据被重复操作。
而前面我们提到过,每个合数由多个素数相乘,则其必有一个最小素数因子,利用这个原理,可以让每个数都被其最小素因子筛去,避免重复操作。
又可知prime中所有素数都是递增的,当i % prime[j] == 0时,i必定可分解为prime[j]乘上另一个素数k (或i == prime[j]),k必定小于a[j++](这个简单分析下就能得出结果);例如,j=1,prime[j]=2,i=4时,i = 2*prime[j],k=2,下一个prime[j]为3,k=2<3=a[2]。后面的几组数同理。
因此,每当 i % prime[j] == 0 时就break,因为后面的几组数里的prime[j]都不会是最小素因子。
当避免了这些重复操作时,成为线性筛法。

voi/d quick_prime()
{
    int total=0;
    memset(prime,0,sizeof(prime)); 
    for(int i=2;i<maxn;i++)
    {
         if(ifprime[i]==0)
          prime[total++]=i;
         for(int j=0;j<total&&i*prime[j]<maxn;j++)
         {
              ifprime[i*prime[j]]=1;
              if(i%prime[j]==0)
               break;
         } 
    }
}

 

 

 

 

矩阵快速幂

这个也是直接模板

参考:https://blog.csdn.net/codeswarrior/article/details/81258928

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct Matrix{
    int m[2][2];
};
Matrix mul(Matrix a,Matrix b){
    Matrix ans;
    for(int i = 0; i < 2; i++){
        for(int j = 0; j < 2; j++){     
            ans.m[i][j] = 0;
            for(int k = 0; k < 2; k++){
                ans.m[i][j] = (ans.m[i][j] + a.m[i][k] * b.m[k][j] % 7) % 7;
            }

        }
    }
    return ans;
}
int main(){
    int A,B,n;
    while(scanf("%d%d%d",&A,&B,&n) != EOF){
        if(A == 0 && B == 0 && n == 0) break;
        if(n <= 2){
            printf("1\n");
            continue;
        }
        Matrix a,ans;
        a.m[0][0] = A;
        a.m[0][1] = B;
        a.m[1][0] = 1;
        a.m[1][1] = 0;
        ans.m[0][0] = ans.m[1][1] = 1;        //===========
        ans.m[0][1] = ans.m[1][0] = 0;        //===========
        n -= 2;
        while(n){
            if(n & 1)
                ans = mul(ans,a);
            n >>= 1;
            a = mul(a,a);
        }
        printf("%d\n",(ans.m[0][0] + ans.m[0][1]) % 7);
    }
    return 0;
}

 

注意:ans这个矩阵是1 0     当他乘  a  b  后仍是后者,之后便是pow操作,这样比同步一个矩阵pow相比里面的参数k不需要-1

                                   0 1                  1  0

 

 

 

欧拉函数

(模板)

不打表:

//euler(x) = x*(1 - 1/p1)*(1 - 1/p2)*(1 - 1/p3)...(1 - 1/pn)   p1 p2..是x的所有的质因子且各不相同  x != 0
//**质因子之和是euler(x)*x / 2
#include <iostream>

using namespace std;

int Euler(int n)
{
    int res = n , a = n;
    for(int i = 2 ; i*i <= a ; i++)
    {
        if(a % i == 0)  //i一定是素数
        {
            res = res / i * (i - 1);  //根据公式
            while(a % i == 0)  //把相同的除数排除
            {
                a /= i;
            }
        }
    }
    if(a > 1)  //最后只剩下 小于4的素数  或者n本身就是素数
        res = res / a *(a - 1);
    return res;
}

int main()
{
    int n;
    while(cin >> n)
    {
        cout << Euler(n) << endl;
    }
}

 

 

 

打表:

//离线打表
//筛选法求欧拉函数,时间复杂度O(nloglogn)
//跟埃式筛法求素数差不多
#include <iostream>

using namespace std;

const int MAXN = 100010;
int a[MAXN];

void init()
{
    for(int i = 1 ; i <= MAXN ; i++)
        a[i] = i;
    a[1] = 0;
    for(int i = 1 ; i <= MAXN ; i++)
    {
        if(a[i] == i)
        {
            for(int j = i ; j <= MAXN ; j += i)
                a[j] = a[j] / i * (i - 1);
        }
    }
}

int main()
{
    init();
    int n;
    while(cin >> n)
    {
        cout << a[n] << endl;
    }
}

 

 

卡特兰数(递推)(代码中有逆元函数求法)

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#define maxn 1000005
#define mod 1000000007

using namespace std;
typedef long long ll;

ll n;
ll f[maxn];
ll ans[maxn];

void Converse(){
    f[1] = 1;
    for(int i = 2; i <= 1000005; i++){
        f[i] = (ll)(mod - mod/i) * f[mod%i] %mod;
    }
    
    ans[1] = 1;
    for(int i = 2 ; i <= 1000005; i++){
        ans[i] = ans[i-1]*(4*i-2)%mod*f[i+1]%mod;
    }
}

int main(){
    
    Converse();
    int T;
    scanf("%d",&T);
    
    for(int i = 1; i <= T; i++){
        scanf("%lld",&n);
        printf("Case #%d:\n%lld\n",i,ans[n]);
    }
    
    return 0;
}

注意:求逆元有四种常用方法,但其他三种都有特殊要求,则优先使用公式法

 

 

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

今日总结:学到了欧拉函数和卡特兰数的知识,要熟悉该模板,遇到数论的题先看公式和所需要的思想,递推等,还有该题隐射的是哪个方程或者函数的求法,遇到公式先推一推,同时数论题需要培养思维,和看到题中所给信息或者公式猜想出原意所指的是运用哪种函数或者方程

明日计划:把数论部分的蓝书看完,周日训练赛结束后下午休息晚上把一部分先总结到博客上

 

posted @ 2019-08-02 20:33  zyddd915  阅读(176)  评论(0编辑  收藏  举报