数论

矩阵乘法

可以把矩阵理解为二维数组,数存在里面,矩阵乘法的规则:A*B=C


实现代码如下:

const int N=50;
int c[N][N];
void multi(int a[N][N],int b[N][N],int n);
{
    memset(c,0,sizeof(c));
    for(int i=1;i<=n;i++)
    {
        for(int k=1;k<=n;k++)
        {
            if(a[i][k]==0) continue;
            for(int j=1;j<=n;j++)
            {
                c[i][j]+=a[i][k]*b[k][j];
            }
        }
    }

}

快速幂和矩阵快速幂

  • 利用位运算判断奇偶性
if(i&1)
{
    奇数....
}
else
{
    偶数...
}
  • 程序终止问题
    exit(0):正常运行程序并退出程序;

exit(1):非正常运行导致退出程序;

return():返回函数,若在主函数中,则会退出函数并返回一值。

快速幂

  • 快速幂原理
    如果当前的指数是偶数,我们把指数拆成两半,得到两个相同的数,然后把这两个相同的数相乘,可以得到原来的数;
    如果当前的指数是奇数,我们把指数拆成两半,得到两个相同的数,此时还剩余一个底数,把这两个相同的数和剩余的底数这三个数相乘,可以得到原来的数。
  • 当n>0代码如下
long long res = 1;
// 进行快速幂运算,n 为当前的指数值,n 为 0 的时候运算结束
while (n) {
    // 用位运算的方式判断 n 是否为奇数,速度更快,等价于 n%2 
    if (n & 1) {
        // 如果 n 是奇数,那么需要将 x 存入运算结果中
        res *= x;
    }
    // 更新当前的 x 的值
    x *= x;
    // 用位运算的方式进行 n/2,速度更快,等价于 n/=2
    n >>= 1;
}

完整的代码如下:

#include <iostream>
#include <cstdlib>
using namespace std;

// 使用快速幂求出 x^n 的值并返回,不考虑高精度,请控制参数范围
double myPow(double x, int n) {
    // 任何不是 0 的数的 0 次幂为 1 
    if (x && n == 0) {
        return 1;
    } else if (x == 0 && n == 0) {
        exit(1);
    }
    // 如果 n 是负数,那么返回结果要进行处理 
    bool judge = false;
    if (n < 0) {
        judge = true;
        n = -n;
    }

    double res = 1;
    while (n) {
        // 用位运算的方式判断 n 是否为奇数,速度更快,等价于 n%2 
        if (n & 1) {
            res =res*res%p;
        }
        x = x*x%p;
        // 用位运算的方式进行 n/2,速度更快,等价于 n/=2
        n >>= 1;
    }
    // n 是负数?1.0/res 否则 res 
    if(judge)
    {
        return 1.0/res;
    }
    else
    {
        return res;
    }
} 

int main() {
    double x;
    int n;

    while (cin >> x >> n) {
        cout << myPow(x, n) << endl << endl; 
    } 

    return 0;
} 

矩阵快速幂模板代码如下

模板题目链接


#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXN 200
#define MOD 1000000007
using namespace std;
typedef long long ll;

int A[MAXN][MAXN];
int res[MAXN][MAXN];
ll tmp[MAXN][MAXN];
int n; ll k;

void mulit(int a[][MAXN],int b[][MAXN])
{
    memset(tmp,0,sizeof(tmp));
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            for(int k=1;k<=n;k++)
            {
                tmp[i][j]=((ll)a[i][k]*b[k][j]%MOD+tmp[i][j])%MOD;
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            a[i][j]=(int)tmp[i][j];
        }
    }

}

void Pow(int a[][MAXN],ll k)
{
    memset(res,0,sizeof(res));
    for(int i=1;i<=n;i++)
    {
        res[i][i]=1;
    }
    while(k)
    {
        if(k&1)
        {
            mulit(res,a);
        }
    mulit(a,a);
    k>>=1;
    }
    
}
int main()
{
    scanf("%d%lld",&n,&k);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            scanf("%d",&A[i][j]);
        }
    }
    Pow(A,k);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            printf("%d ",res[i][j]);
        }
        cout<<endl;
    }
}

(代码借用了https://blog.csdn.net/wust_zzwh/article/details/52058209)

利用矩阵快速幂求斐波那契数列

利用矩阵快速幂求递推数列的关键是找到合适的递推关系式
如此大佬博客中所推蓝桥杯 算法提高 递推求值(矩阵快速幂)详解
利用矩阵快速幂求斐波那契数列的模板题链接P1939 【模板】矩阵加速(数列)
模板题代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXN 200
#define MOD 1000000007
#define ll long long
using namespace std;

ll n;
ll A[MAXN][MAXN];
ll B[MAXN][MAXN];
ll res[MAXN][MAXN];
ll tmp[MAXN][MAXN];

void mulit(ll a[][MAXN],ll b[][MAXN])
{
    memset(tmp,0,sizeof(tmp));
    for(int i=1;i<=6;i++)
    {
        for(int j=1;j<=6;j++)
        {
            for(int k=1;k<=6;k++)
            {
                tmp[i][j]=((a[i][k]*b[k][j])%MOD+tmp[i][j])%MOD;
            }
        }
    }
    for(int i=1;i<=6;i++)
    {
        for(int j=1;j<=6;j++)
        {
            a[i][j]=tmp[i][j];
        }
    }
}

void pow(ll a[][MAXN],ll k)
{
    memset(res,0,sizeof(res));
    for(int i=1;i<=6;i++)
    {
        res[i][i]=1;
    }
    
    while(k)
    {
        if(k&1)
        {
            mulit(res,a);
//			printf("k=%lld\n",k);
//		printf("res=%lld\n",res[1][1]);
        }
        mulit(a,a);
        k>>=1;
    }
}
int main()
{
    scanf("%lld",&n);
    if(n==1||n==2)
    {
        printf("1");
        return 0;
    }
    A[1][1]=1;
    A[1][2]=1;
    A[2][1]=1;
    A[2][2]=0;
    B[1][1]=1;
    B[2][1]=1;
    pow(A,n-2);
//	cout<<1;
    mulit(res,B);
    
    printf("%lld ",res[1][1]);
    return 0;
}
 

类似题及代码如下:
P1939 【模板】矩阵加速(数列)
代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 7
#define MOD 1000000007
#define ll long long
using namespace std;

ll n;
ll A[7][7];
ll B[7][7];
ll res[7][7];
ll tmp[7][7];

void mulit(ll a[][MAXN],ll b[][MAXN])
{
	memset(tmp,0,sizeof(tmp));
	for(int i=1;i<=6;i++)
	{
		for(int j=1;j<=6;j++)
		{
			for(int k=1;k<=6;k++)
			{
				tmp[i][j]=((a[i][k]*b[k][j])%MOD+tmp[i][j])%MOD;
			}
		}
	}
	for(int i=1;i<=6;i++)
	{
		for(int j=1;j<=6;j++)
		{
			a[i][j]=tmp[i][j];
		}
	}
}

void pow(ll a[][MAXN],ll k)
{
	memset(res,0,sizeof(res));
	for(int i=1;i<=6;i++)
	{
		res[i][i]=1;
	}
	
	while(k)
	{
		if(k&1)
		{
			mulit(res,a);
//			printf("k=%lld\n",k);
//		printf("res=%lld\n",res[1][1]);
		}
		mulit(a,a);
		k>>=1;
	}
}

int main()
{
	ll T;
	scanf("%lld",&T);
	while(T--)
	{
		scanf("%lld",&n);
		if(n==1||n==2||n==3)
		{
			printf("1\n");
			continue;
		}
		A[1][1]=1;
		A[1][2]=0;
		A[1][3]=1;
		A[2][1]=1;
		A[2][2]=0;
		A[2][3]=0;
		A[3][1]=0;
		A[3][2]=1;
		A[3][3]=0;
		B[1][1]=1;
		B[2][1]=1;
		B[3][1]=1;
		pow(A,n-2);
		mulit(res,B);
		printf("%lld\n",res[2][1]);
	}
}

有一个小结论叫做gcd(F(n),F(m))=F(gcd(n,m))

P1306 斐波那契公约数
代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXN 9
#define MOD 100000000
#define ll long long 
using namespace std;

ll n,m;
ll A[MAXN][MAXN];
ll B[MAXN][MAXN];
ll res[MAXN][MAXN];
ll tmp[MAXN][MAXN];

void mulit(ll a[][MAXN],ll b[][MAXN])
{
	memset(tmp,0,sizeof(tmp));
	for(int i=1;i<=5;i++)
	{
		for(int j=1;j<=5;j++)
		{
			for(int k=1;k<=5;k++)
			{
				tmp[i][j]=((a[i][k]*b[k][j])%MOD+tmp[i][j])%MOD;
			}
		}
	}
	for(int i=1;i<=5;i++)
	{
		for(int j=1;j<=5;j++)
		{
			a[i][j]=tmp[i][j]; 
		} 
	}
}

void pow(ll a[][MAXN],ll k)
{
	memset(res,0,sizeof(res));
	for(int i=1;i<=5;i++)
	{
		res[i][i]=1;
	}
	while(k)
	{
		if(k&1)
		{
			mulit(res,a);
		}
		mulit(a,a);
		k>>=1;
	}
}

int main()
{
	scanf("%lld%lld",&n,&m);
	A[1][1]=1;
	A[1][2]=1;
	A[2][1]=1;
	A[2][2]=0;
	B[1][1]=1;
	B[2][1]=1;
	ll num;
	num=__gcd(n,m);
//	printf("num=%lld",num);
	if(num==1||num==2)
	{
		printf("1");
		return 0;
	}
	pow(A,num-2);
	mulit(res,B);
	printf("%lld",res[1][1]);
	
}

乘法逆元

乘法逆元的几种计算方法
线性推核心代码如下:

	inv[1]=1;
	printf("1\n");
	for(int i=2;i<=n;i++)
	{
		inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;
		printf("%lld\n",inv[i]); 
	}

拓展欧几里得算法求乘法逆元代码如下

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>

using namespace std;

void exgcd(int a,int b,int &x,int &y)
{
	if(b==0)
	{
		x=1;
		y=0;
		return;
	}
	else
	{
		int tx,ty;
		exgcd(b,a%b,tx,ty);
		y=tx-(a/b)*ty;
		x=ty;
		return;
	}
}

int main()
{
	int n,p;
	scanf("%d%d",&n,&p);
	for(int i=1;i<=n;i++)
	{
		int x,y;
		exgcd(i,p,x,y);
		if(x<0)
		{
			x=x+p;
		 } 
		 printf("%d\n",x);
	}
 } 

费马小定理代码如下:

ll Pow(ll a, ll k)
{
	ll res=1;
	while(k)
	{
		if(k&1)
		{
			res=res*a%MOD;
		}
		a=a*a%MOD;
		k>>=1;
	}
	return res;
}

模板题链接:P3811 【模板】乘法逆元
拓展题链接:P2613 【模板】有理数取余
拓展题代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
#define MAXN 100010 


using namespace std;
const int MOD=19260817; 
ll a,b;
char str[MAXN];

ll Pow(ll a,ll k)
{
    ll res=1;
    while(k)
    {
        
        if(k&1)
        {
            res=res*a%MOD;
        }
        a=a*a%MOD;
        k>>=1;
    }
    return res;
}

int main()
{
    
     scanf( "%s", str+1 );
    for( int i = 1, en = (int)strlen( str+1 ); i <= en; ++i )
        a = ( a * 10 + str[i] - '0' ) % MOD;
    scanf( "%s", str+1 );
    for( int i = 1, en = (int)strlen( str+1 ); i <= en; ++i )
        b = ( b * 10 + str[i] - '0' ) % MOD;
    if(b==0)
    {
        printf("Angry!");
        return 0;
    }
    ll ans=(Pow(b,MOD-2)%MOD*a)%MOD;
    printf("%lld",ans);
 } 
posted @ 2019-05-31 09:55  LITTLESUN_wl  阅读(251)  评论(0)    收藏  举报