前言
周六学了“数论知识杂谈”,重点是【矩阵加速】,做了几道相关的题目,还好感觉比较简单,都能理解,只是自己不熟悉结构体,导致板子不能特别顺利的打出来_(:з」∠)_
来总结总结
知识
一、更相减损术
一种可以求两个数的最大公因数的方法,最早源自《九章算术》
递归和循环都可以实现
其实有种改良的方法:
两数a,b一直大减小直至差和减数相等,最后的差值即gcd(a,b)
代码WA了,不知原因,改好以后上传...
类似于辗转相除法:
二、拉姆齐定理
这里简单提及,详见PPT,不是重点
三、矩阵及其运算
只有方阵才有行列式,即det(A)或|A|
没有什么好说的,直接写例题吧...orz...
(一)矩阵A*B
题目描述
输入
输出
Sample Input
2 3 1 2 3 3 2 1 2 1 1 2 2 3 3
Sample Input
14 14 10 10
分析
有两种做法
(一)朴素算法:直接将矩阵的行列逐个相乘相加即可
(二)结构体:一种极其“骚操作”的做法(板子来源于一篇博客,自己有稍作改动)
代码:
(一)朴素版
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=100+5;
ll a[MAXN+5][MAXN+5],b[MAXN+5][MAXN+5];
ll n,m,p;
ll Matrix(ll x,ll y)
{
ll ret=0;
for(int i=1;i<=m;i++)
ret+=a[x][i]*b[i][y];
return ret;
}
int main()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%lld",&a[i][j]);
scanf("%lld",&p);
for(int i=1;i<=m;i++)
for(int j=1;j<=p;j++)
scanf("%lld",&b[i][j]);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=p;j++)
if(j==1)
printf("%lld",Matrix(i,j));
else
printf(" %lld",Matrix(i,j));
printf("\n");
}
return 0;
}
(二)结构体版
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=100;
struct Matrix
{
int n,m;
int c[MAXN+5][MAXN+5];
Matrix(){memset(c,0,sizeof(c));}
void read()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&c[i][j]);
}
Matrix operator*(const Matrix& a)
{
Matrix r;
r.n=n;r.m=a.m;
for(int i=1;i<=r.n;i++)
for(int j=1;j<=r.m;j++)
for(int k=1;k<=m;k++)
r.c[i][j]+=c[i][k]*a.c[k][j];
return r;
}
void print()
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
if(j==1)
printf("%d",c[i][j]);
else
printf(" %d",c[i][j]);
printf("\n");
}
}
}A,B,C;
int main()
{
scanf("%d%d",&A.n,&A.m);
B.n=A.m;
A.read();
scanf("%d",&B.m);
B.read();
C=A*B;
C.print();
return 0;
}
(二)Fibonacci 第 n 项(矩阵加速)
题目描述
输入
输入n,m
对于100%的数据,
输出
Sample Input
5 1000
Sample Output
5
分析
设计两个矩阵:一个答案矩阵,一个单位矩阵
答案矩阵:ans[ 1,1 ] 即 ans[ f1,f2 ]
单位矩阵:
将单位矩阵的ans矩阵相乘,神奇的事情发生了——ans' [ f2,f1+f2 ]
由此可以得出:要求 f( n ) 即将答案矩阵初始化后乘上单位矩阵的(n-1)次方,答案就是最后的ans[1][1],中途不要忘了取模,因为数特别大
(超级神奇,不知道是那个大佬想出来的方法)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=100;
ll k,mod;
struct Matrix
{
ll n,m;
ll c[MAXN+5][MAXN+5];
Matrix(){memset(c,0,sizeof(c));}
Matrix operator*(const Matrix& a)
{
Matrix r;
r.n=n;r.m=a.m;
for(int i=1;i<=r.n;i++)
for(int j=1;j<=r.m;j++)
for(int k=1;k<=m;k++)
r.c[i][j]=(r.c[i][j]+c[i][k]*a.c[k][j]%mod)%mod;
return r;
}
};
Matrix pow(Matrix a,ll b)
{
Matrix res;
res.n=res.m=2;
for(int i=1;i<=res.n;i++)
res.c[i][i]=1;//初始化-单位矩阵
for(;b;b>>=1)//b/=2;
{
if(b&1)//b%2!=0
res=res*a;
a=a*a;
}
return res;
}
int main()
{
scanf("%lld%lld",&k,&mod);
Matrix A;
A.n=A.m=2;
A.c[1][2]=A.c[2][1]=A.c[2][2]=1;
//[0 1]
//[1 1]
A=pow(A,k-1);
Matrix ans;
ans.n=1;ans.m=2;
ans.c[1][1]=ans.c[1][2]=1;//ans矩阵初始化:[1,1]即[f1,f2]
ans=ans*A;
printf("%lld\n",ans.c[1][1]%mod);
return 0;
}
(三)Fibonacci前n项和(矩阵加速)
题目描述
大家都知道Fibonacci数列吧,f1=1,f2=1,f3=2,f4=3,…,fn=fn-1+fn-2,现在问题很简单,输入n和m,求{fn} 的前n项和Sn mod m。
输入
输入n,m
输出
输出前n项和Sn mod m
Sample Input
5 1000
Sample Output
12
分析
只用在上一题的基础上变化一下ans矩阵和单位矩阵即可,即:
ans[ s1,f1,f2 ],
相乘即可得到:ans[ s2,f2,f3 ]
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=100;
ll k,mod;
struct Matrix
{
ll n,m;
ll c[MAXN+5][MAXN+5];
Matrix(){memset(c,0,sizeof(c));}
Matrix operator*(Matrix a)
{
Matrix r;
r.n=n;r.m=a.m;
for(int i=1;i<=r.n;i++)
for(int j=1;j<=r.m;j++)
for(int k=1;k<=m;k++)
r.c[i][j]=(r.c[i][j]+c[i][k]*a.c[k][j]%mod)%mod;
return r;
}
};
Matrix pow(Matrix a,ll b)
{
Matrix res;
res.n=res.m=3;
for(int i=1;i<=res.n;i++)
res.c[i][i]=1;//初始化-单位矩阵
for(;b;b>>=1)//b/=2;
{
if(b&1)//b%2!=0
res=res*a;
a=a*a;
}
return res;
}
int main()
{
scanf("%lld%lld",&k,&mod);
Matrix A;
A.n=A.m=3;
A.c[1][1]=A.c[2][1]=A.c[2][3]=A.c[3][1]=A.c[3][2]=A.c[3][3]=1;
A=pow(A,k-2);
Matrix ans;
ans.n=1;ans.m=3;
ans.c[1][1]=2;
ans.c[1][2]=ans.c[1][3]=1;//ans矩阵初始化:[2,1,1]即[s1,f1,f2]
ans=ans*A;
printf("%lld\n",ans.c[1][1]%mod);
return 0;
}
(四)TR的数列(矩阵加速)
TR是本社团的一个同学,老师很皮地拿来当题目了233
题目描述
TR非常喜欢数学,经常一个人拿出草稿纸研究奇奇怪怪的数学问题,最近,他突然对数列产生了兴趣,他找到一个数列,类似于斐波拉契,即:Tn=1*f1+2*f2+3*f3+……+n*fn (fn为斐波拉契的第n项值)
现在TR想请你帮忙求Tn%m的值
输入
两个用空格隔开的整数n和m
输出
Tn mod m的值
Sample Input
5 5
Sample Output
1
分析
老套路,构造符合题意的ans矩阵和单位矩阵(构造的方式有很多种)
(感觉对于自己这种思路不开阔的小白来说,能不能顺利构造出来还说不准_(:з」∠)_)
ans[ 1,1,2,1,1 ] 即 ans[ T1,f1*1,f2*2,f1,f2 ]
目标是——ans' [ T2,f2*2,f3*3,f2,f3 ]
构造得单位矩阵:
(矩阵真难打orz)
乘一下,算一算结果,感受感受,得到:
ans' [ T1+f2*2,f2*2,3*(f1+f2),f1+f2 ] 即 ans'[ T2,f2*2,f3*3,f2,f3 ]
即探究:ans [ T(i) , f(i) * i , f(i+1)*(i+1) , f(i) , f(i+1) ] 怎么得到:
ans[ T(i+1) , f(i+1)*(i+1) , f(i+2)*(i+2) , f(i+1) , f(i+2) ]
而T(i+1) = T(i) + f(i+1)*(i+1)
f(i+1) = f(i) + f(i-1)
f(i+2)*(i+2)=(i+2)*[ f(i+1) + f(i) ]
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=100;
ll k,mod;
struct Matrix
{
ll n,m;
ll c[MAXN+5][MAXN+5];
Matrix(){memset(c,0,sizeof(c));}
Matrix operator*(Matrix a)
{
Matrix r;
r.n=n;r.m=a.m;
for(int i=1;i<=r.n;i++)
for(int j=1;j<=r.m;j++)
for(int k=1;k<=m;k++)
r.c[i][j]=(r.c[i][j]+c[i][k]*a.c[k][j]%mod)%mod;
return r;
}
};
Matrix pow(Matrix a,ll b)
{
Matrix res;
res.n=res.m=5;
for(int i=1;i<=res.n;i++)
res.c[i][i]=1;//初始化-单位矩阵
for(;b;b>>=1)//b/=2;
{
if(b&1)//b%2!=0
res=res*a;
a=a*a;
}
return res;
}
int main()
{
scanf("%lld%lld",&k,&mod);
Matrix A;
A.n=A.m=5;
A.c[1][1]=A.c[2][3]=A.c[3][1]=A.c[3][2]=A.c[3][3]=1;
A.c[4][3]=2;
A.c[4][5]=A.c[5][3]=A.c[5][4]=A.c[5][5]=1;
A=pow(A,k-1);
Matrix ans;
ans.n=1;ans.m=5;
ans.c[1][1]=1;
ans.c[1][2]=1;ans.c[1][3]=2;ans.c[1][4]=ans.c[1][5]=1;
//ans矩阵初始化:[1,1,2,1,1]即[T1,f1*1,f2*2,f1,f2]
ans=ans*A;
printf("%lld\n",ans.c[1][1]%mod);
return 0;
}