基本介绍
作用:便于快速求
O
(
l
o
g
n
)
{O(logn)}
O(logn)具有线性递推关系的数量关系或数列.
前置知识:快速幂和矩阵乘法.
例题推荐
1.斐波拉契数列
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1000000007;
ll n,k;
ll read()
{
ll i=0,f=1;char ch;
while(!isdigit(ch))
{
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch))
{
i=(i<<3)+(i<<1)+(ch^48);
ch=getchar();
}
return i*f;
}
struct jvzhen{
ll q[12][12];}A,I;
jvzhen operator*(const jvzhen &x,const jvzhen &y)
{
jvzhen c;
memset(c.q,0,sizeof(c.q));
for(int i=1;i<=2;i++)
for(int j=1;j<=2;j++)
for(int p=1;p<=2;p++)
c.q[i][j]=(c.q[i][j]+(x.q[i][p]*y.q[p][j])%mod)%mod;
return c;
}
int main()
{
n=read();
if(n==1||n==2)
{
cout<<1<<endl;
return 0;
}
A.q[1][1]=1,A.q[1][2]=1,A.q[2][1]=1;
I.q[1][1]=1,I.q[1][2]=1,k=n-2;
while(k>0)
{
if(k%2==1)I=I*A;
A=A*A;
k=k>>1;
}
cout<<I.q[1][1]<<endl;
return 0;
}
2.【模板】矩阵加速(数列)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1000000007;
ll n,k;
int T;
inline ll read(){
ll i=0;ll f=1;char ch;
while(!isdigit(ch))
{
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch)){
i=(i<<3)+(i<<1)+(ch^48);ch=getchar();}
return i*f;
}
struct jvzhen{
ll q[105][105];}A,I;
jvzhen operator*(const jvzhen &x,const jvzhen &y)
{
jvzhen c;
memset(c.q,0,sizeof(c.q));
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
for(int p=1;p<=3;p++)
c.q[i][j]=(c.q[i][j]+x.q[i][p]*y.q[p][j]%mod)%mod;
return c;
}
int main()
{
T=read();
while(T--)
{
I.q[1][1]=1,I.q[1][2]=1,I.q[1][3]=1;
memset(A.q,0,sizeof(A.q));
n=read();
if(n<4)
cout<<"1"<<endl;
else
{
A.q[1][1]=1,A.q[1][2]=1;
A.q[2][3]=1,A.q[3][1]=1;
k=n-3;
while(k>0)
{
if(k%2==1)I=I*A;
A=A*A;
k=k>>1;
}
cout<<I.q[1][1]<<endl;
}
}
return 0;
}
3.斐波那契公约数
传送门
要用到数学知识:
G
c
d
(
f
[
m
]
,
f
[
n
]
)
=
G
c
d
(
m
,
n
)
{Gcd(f[m],f[n])=Gcd(m,n)}
Gcd(f[m],f[n])=Gcd(m,n).
#include<bits/stdc++.h>
using namespace std;
const int mod=100000000;
int a,b,k;
struct juzhen{
long long p[15][15];}A,I;
juzhen operator*(const juzhen &x,const juzhen &y)
{
juzhen z;
memset(z.p,0,sizeof(z.p));
for(int i=1;i<=2;i++)
for(int j=1;j<=2;j++)
for(int k=1;k<=2;k++)
z.p[i][j]=(z.p[i][j]+(x.p[i][k]*y.p[k][j])%mod)%mod;
return z;
}
int gcd(int a,int b)
{
if(b==0)return a;
gcd(b,a%b);
}
int main()
{
scanf("%d%d",&a,&b);
if(b>a)swap(a,b);
k=gcd(a,b);
for(int i=1;i<=2;i++)
I.p[i][1]=1;
A.p[1][1]=1,A.p[1][2]=1;
A.p[2][1]=1,A.p[2][2]=0;
if(k<=2)
{
puts("1");
return 0;
}
while(k>0)
{
if(k%2==1)I=I*A;
A=A*A;
k=k>>1;
}
cout<<I.p[1][2]<<endl;
return 0;
}
总结
该模板不常考,但值得思考拓展.里面涉及了一些 u s e f u l {useful} useful高等数学知识.然后,就可以去感受狂A题的快感了!