数论

数论

牛顿迭代法

\[x[n]=x[n-1]-f(x[n-1])/f{'}(x[n-1]) \]

将算出的x[n]不断带入x[n-1]的位置重新计算,使x的值处于答案要求的精确度范围即可

算数基本定理

\[N=p^{a1}_{1}p^{a2}_{2}p^{a3}_{3}...p^{an}_{n} \]

1.自然数N(N>1)可以被分解为n个质数p_k(1...n)的a_k(1...n)次方相乘

2.每个自然数N最多只有一个超过sqrt(N)的因数

反证法,如果有两个以及以上的因数超过sqrt(N),那么则相乘大于N

因此在将N分解为多个质因数相乘的时候可以这样编程

#include <bits/stdc++.h>
using namespace std;

int main()
{
    int n;
    cin >> n;
    while (n--)
    {
        long long a;
        cin >> a;
    for(int i=2;i<=a/i;i++ )//因为只有最多一个因数>sqrt(N),所以i<=a/i (1)
    {					//不过a是变动的,每次a都会除掉一个质数p_k(1...n)的a_k(1...n)次方(2)
        				//每次将p_k(1...n) a_k(1...n)次方除完以后输出,之后剩下来的数的因数大小不大于当前的i
        				//因为小于i的质因数都被除尽了 见(2),然后也符合(1),这样能把问题从求N,变成求N/p1_a1....							//以此类推
        		
        int countn=0;
        if(a%i==0)
        {
            while(a%i==0)
            {
                a/=i;
                countn++;
            }
        printf("%d %d\n",i,countn);
        }
    }
  if(a>1)	//如果有没有除尽的因数那就是那个唯一的因数>sqrt(N)
  printf("%d 1\n",a);
  printf("\n");
    
    }
    return 0;
}

质数筛法

//埃氏筛    O(nloglogn) 小数据完爆欧拉筛
int vis[MAXN], prime[MAXN], p = 0;
void primea()
{
    //memset(vis,0,sizeof(vis));
    //memset(prime,0,sizeof(prime));

    vis[1] = 1;
    for (int i = 2; i <= MAXN; i++)
        if (!vis[i])
        {
            prime[++p] = i;
            for (int j = i * i; j <= MAXN; j += i) //把i的倍数全标记为合数,因为其(2~i-1)倍已经被标记,所以从i倍开始
                vis[j] = 1;
        }
}

//欧拉筛 线性筛 O(n)数据大的时候会比较优
#define MAXN (int)10e6+5
long long  isprime[MAXN],prime[MAXN],countn=0;
void primeo(int n)
{
   for(int i=2;i<=n;i++)
{
    
    if(!isprime[i])
    {
        prime[++countn]=i;//首先小于i的合数都是由(质数prime[j]*小于该数的数)(1)组成
        //而小于i的数都被(prime[1]*小于i的数 prime[2]*小于i的数....)筛过了,其质因数都包含在小于i的数里面,
        //每个质数p都会*(该质数---小于i的数)来筛掉p的倍数,以及*(2---该质数 中的质数)筛掉p的倍数,而p*小于p大于2的合数,也会通过之后的循环筛掉p的倍数
        //之后小于p的质数会*大于p小于i的数,从而总能筛掉p的所有倍数
    }
    
    for(int j=1;j<=countn&&i*prime[j]<=n;j++)
    {
        isprime[prime[j]*i]=1;//由于每次i%prime[j]的时候总能够break,因此被筛掉的数的最小质因数不会大于最后一个
        						//prime[j],否则在被筛的i*prime[j]的最小质因子不是prime[j],
        						//而是i中的某个已经被遍历过的因子,则会被不同因子重复筛掉
        if(i%prime[j]==0)
        break;
    }
    
    
}
    
}

模运算规则

image-20211001140842095

快速幂(取模)

long long ksm(long long a,long long b,long long p)
{
    long long sum=1;
    while(b)
    {

        if(b&1)
    {
    sum*=a%p;
    sum%=p;
    b--;
    }
    a%=p;
    // cout<<a<<"m"<<endl;
    a*=a;
    // cout<<a<<endl;
    b/=2;
    }
    
    return sum;
}

费马定理

\[b^{p-1}\equiv1 \quad(mod \quad p) \\gcd(p,b)==1 \]

同余恒等式求逆元

image-20211001171708597

\[a*b*b^{-1}\equiv a \quad(mod \quad m)\quad ①\\ b*b^{-1}\equiv1 \quad(mod \quad m)\quad② \\b^{p-1}\equiv b*b^{p-2}\equiv1 \quad(mod \quad m)③ \\ b^{-1}=x=b^{p-2} \]

题目条件:m为质数

1.a与 m互质时a可以消去 ①--->②

2.③式成立的前提是m与b互质,将bp-2分离出作为逆元,若m与b不互质则无逆元因为m为质数,b与其唯一可能非1公因数是m,若不互质则bp-1%m==0≠1

2.在一个群内一种运算单位元e唯一,因此此时e恒等b*b^-1恒等1 在mod p的"条件"下

int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        long long a,b,p;
        cin>>a>>p;
        if(a%p)
        cout<<ksm(a,p-2,p)<<endl;
        else {
            printf("impossible\n");
        }
        
    }
    
    return 0;
}

同余式

1.同余式可以逐项相加

img

img

2.同余式可以逐项相乘。

img

,则

img

同余式两边的数和模可以同时乘上一个整数。

img

,则

img

4.同余式两边的数和模可以同时被它们任一公约数除。

即不需要m与d是互质,但是要是m中有与ab的公因式d,三个数都消去d

img

,则

img

如果同余式对于模m成立,那么它对于m的任意约数相等的模d也成立。

img

,则

img

5.如果同余式一边上的数和模能被某个数除尽,则同余式的另一边的数也能被这个数除尽。

img

,则

img

6.同余式一边上的数与模的最大公约数,等于另一边上的数与模的最大公约数。 [2]

img

,则

img

约数个数

给定 n 个正整数 ai,请你输出这些数的乘积的约数个数,答案对 10^9+7 取模。

输入格式

第一行包含整数 n。

接下来 n 行,每行包含一个整数 ai。

输出格式

输出一个整数,表示所给正整数的乘积的约数个数,答案需对10^9+7 取模。

数据范围

1≤n≤100
1≤ai≤2×10^9

#include<bits/stdc++.h>
#define MOD int (1e9+7)
using namespace std;
int a[105];
map<int ,long long > v;

int main()
{
    
    int n;
    long long sum=1;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
     
         for(int j=2;j<=a[i]/j;j++)
            {
                if(a[i]%j==0)
                {
                    if(v[j]==0)
                    v[j]=1;
                    while(a[i]%j==0)
                    {
                        a[i]/=j;
                        v[j]++;
                    }
                }
            }
            
            if(a[i]>1){
                if(v[a[i]]==0)
                 v[a[i]]=1;
                v[a[i]]++;
            }
            
    }

    for(map<int ,long long>::iterator i=v.begin();i!=v.end();i++)
    {
        sum*=i->second;
        sum%=MOD;
        
        // cout<<i->first<<" " <<i->second<<endl;
    }
    cout<<sum<<endl;
    
    return 0;
}

约数之和

题目同上,但是约数之和

推出公式

\[N=p^{a1}_{1}p^{a2}_{2}p^{a3}_{3}...p^{an}_{n}\\(p_{1}^0+p_{1}^1+p_{1}^2+...p_1^{a1})*(p_{2}^0+p_{2}^1+p_{2}^2+...p_2^{a2})*...(p_{n}^0+p_{n}^1+p_{n}^2+...p_n^{an})\\ \]

此处p_x是质数,是上述所有数乘积分解成质数因数(基于算数基本定理)的结果 a_x是质数因数的次方

第二个式子的推出是通过先加几个然后看规律得到的

背后的原理还是像二项式展开式那样,从每个因子中选择一个p_x^k,然后相乘得到一个约数再加到总和里面(不同的选择产生的约数是唯一的),遍历所有的可能.因此是这样的式子

然后就能用等比数列之和公式来做,

\[(p_{k}^0+p_{k}^1+p_{k}^2+...p_k^{ak})=p_k^0*\frac{p_k^{a_k+1}-1}{p-1} \]

注意取模,由于有除法运算,先判定p-1与m是否互质,若是则可以通过求p-1的逆元 (p-1)^(-1)转换成取模结果一致的乘法运算

\[p_k^0*\frac{p_k^{a_k+1}-1}{p-1} \quad(mod \quad m)\equiv p_k^0*({p_k^{a_k+1}-1})*(p-1)^{-1} \]

否则

\[p-1\equiv 0\quad (mod \quad m)\\ p\equiv 1\quad (mod \quad m)\\ p^k\equiv 1\quad (mod \quad m)\\ p_{k}^0+p_{k}^1+p_{k}^2+...p_k^{ak} \quad mod \quad m=(ak+1)\quad mod \quad m \]

欧拉函数

\[\phi(N)=N*\frac{p_1-1}{p_1}*\frac{p_2-2}{p_2}...*\frac{p_m-1}{p_m} \]

  1. p_k为N的质因数

  2. φ(N)表示1-N中的与N互质的数

  3. 本式子来源于N-N/p1-N/p2...+N/(p1p2)+N/(p1p3)...-N/(p1p2p3)

-N/(p1p2p4)...=N(1-1/p1)(1-1/p2)(1-1/p3)...(1-1/p_m)

每次从第k个因式中选择1相当于不将p_k作为分母因数,反之选择1/pk就是作为因数,而式子符号可以由乘入的负号决定

欧拉筛法求欧拉函数

欧拉筛法O(n),可以通过最小质因子*某个倍数筛掉1~n的合数,得到质数,而欧拉函数φ(N)与它的因子的φ(x)有关系,

当φ(N=i*prime[j])

1.i%prime[j]==0

φ(N)=φ(i)*prime[j]

2.i%prime[j]!=0

φ(N)=φ(i)prime[j](prime[j]-1)/(prime[j])

线性方程组

题目描述

已知 n元线性一次方程组。

\[\begin{cases} a_{1, 1} x_1 + a_{1, 2} x_2 + \cdots + a_{1, n} x_n = b_1 \\ a_{2, 1} x_1 + a_{2, 2} x_2 + \cdots + a_{2, n} x_n = b_2 \\ \cdots \\ a_{n,1} x_1 + a_{n, 2} x_2 + \cdots + a_{n, n} x_n = b_n \end{cases} \]

请根据输入的数据,编程输出方程组的解的情况。

输入格式

第一行输入未知数的个数 n
接下来 n行,每行 n + 1 个整数,表示每一个方程的系数及方程右边的值。

输出格式

如果有唯一解,则输出解(小数点后保留两位小数)。

如果方程组无解输出 -1; 如果有无穷多实数解,输出 0;

当前列:前几列已被消成有唯一元素1,当前正在消的列

选择一行:选择的行应该是未被选择过的行

高斯约旦消元法,从最左列到最右列消元,每次选择一行把它的当前列除成1,然后拿这行通过初等变换去消别的行。

1.注意每次选择当前列元素最大的那行,这样能保证精确度,浮点数精度丢失问题。

2.当前列为i时选定i行作为对比对象,最后将当前列最大的行交换到第i行上,这样可以保证每次对比的是没有使用过的行

3.fabs()浮点数取绝对值,小于某个范围判定为该数

#include <bits/stdc++.h>
#define eps 1e-5
using namespace std;
double a[110][110];
int vis[110];
int n;
void solve()
{

    for (int i = 1; i <= n; i++)
    {
        int m = i;
        for (int k = i + 1; k <= n; k++)
        {

            if (fabs(a[k][i]) > fabs(a[m][i]))
            {
                m = k;
            }
        }

        if (fabs(a[m][i]) < eps)
            continue;
        for (int j = 1; j <= n + 1; j++)
            swap(a[m][j], a[i][j]);

        double temp = a[i][i];
        for (int j = 1; j <= n + 1; j++)
        {
            a[i][j] /= temp;
        }

        for (int k = 1; k <= n; k++)
        {
            if (k != i)
            {
                double t = -(a[k][i] / a[i][i]);
                for (int j = 1; j <= n + 1; j++)
                {
                    a[k][j] += t * a[i][j];
                }
            }
        }
    }

    // for (int j = 1; j <= n; j++)
    // {
    //     for (int i = 1; i <= n + 1; i++)
    //     {
    //         cout<<a[j][i]<<" ";
    //     }
    // cout<<'\n';
    // }
}
void check()
{
    int r = 0;
    for (int i = 1; i <= n; i++)
    {
        bool flag = 0;
        for (int j = 1; j <= n; j++)
        {
            if (fabs(a[i][j] - 0) > eps)
            {
                r++;
                flag = 1;
                break;
            }
        }

        if (!flag && fabs(a[i][n + 1] - 0) > eps)
        {
            printf("-1\n");
            return;
        }
    }
    // cout<<r<<endl;
    if (r < n)
    {
        printf("0\n");
        return;
    }

    if (r == n)
    {
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= n; j++)
            {
                if (fabs(a[i][j] - 0) > eps)
                    printf("x%d=%.2lf\n", j, a[i][n + 1]);
            }
        }

        return;
    }
}
int main()
{

    cin >> n;
    for (int j = 1; j <= n; j++)
        for (int i = 1; i <= n + 1; i++)
        {
            cin >> a[j][i];
        }
    solve();
    check();
    return 0;
}
posted @ 2021-10-21 22:08  多巴胺不耐受仿生人  阅读(61)  评论(0)    收藏  举报