暂时还有题目没做出来,把已经写出来的题目先整理出来吧,顺便理一下矩阵快速幂的常见用法和思路,为做下面的题目做一个缓冲
由等式求递推矩阵,加速
F-M斐波那契数列
题意: 类似斐波那契数列给出一种数列的新定义:
F[0] = a
F[1] = b
F[n] = F[n-1] * F[n-2] ( n > 1 )
给出a, b, n,( 0 <= a, b, n <= 10^9),要求输出F[n]的值%(1e9+7)
思路: 写出几个F[n],a,b, ab, ab2, a2b3, a3b5, a5b8……
可以 观察出 这个序列的次数是斐波那契数列,由于n比较大,用矩阵快速幂加速一下~ 然后因为斐波那契数做次数可能比较大,所以用到费马小定理,取余(1e9+6)
之后问题的 关键只要写出斐波那契数列的递推矩阵 就可以啦~ 所以关键如图
话说这个名字前的M是代表matrix嘛? 正式比赛的时候会给这种提示咩???
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long LL;
struct Squ{
LL s[2][2]; //一开始WA了还找不出是哪里,后来看到这里设成了int觉得都不想对自己说什么了……
};
Squ e,t;
const LL mod=1e9+7;
LL a,b,n;
LL quickpow(LL a,LL k){
a%=mod;
LL ans=1;
while(k){
if(k&1)
ans=(ans*a)%mod;
a=(a*a)%mod;
k/=2;
}
return ans;
}
Squ mul(Squ a,Squ b){
Squ ans;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++){
ans.s[i][j]=0;
for(int k=0;k<2;k++){
ans.s[i][j]+=a.s[i][k]*b.s[k][j];
ans.s[i][j]%=(mod-1);
}
}
return ans;
}
Squ squickpow(Squ a,LL k){
Squ ans=e;
while(k){
if(k&1) ans=mul(ans,a);
a=mul(a,a);
k/=2;
}
return ans;
}
int main(){
for(int i=0;i<2;i++) //初始化单位矩阵
for(int j=0;j<2;j++)
if(i==j) e.s[i][j]=1;
else e.s[i][j]=0;
t.s[0][0]=t.s[0][1]=t.s[1][0]=1;
t.s[1][1]=0;
while(~scanf("%lld%lld%lld",&a,&b,&n)){
a%=mod,b%=mod;
if(n==0){ //两种情况单独处理
printf("%lld\n",a%mod); continue;
}
else if(n==1){
printf("%lld\n",b%mod); continue;
}
Squ ans=squickpow(t,n-2); //对应图中的n-2次
LL fn=(ans.s[0][0]+ans.s[0][1])%(mod-1);
LL fn1=(ans.s[1][0]+ans.s[1][1])%(mod-1);
long long res=(quickpow(a,fn1)*quickpow(b,fn))%mod;
printf("%lld\n",res);
}
return 0;
}
Recursive sequence
题意:
告诉F[1],F[2]和n,(F[1],F[2],n<2^31), 求F[n]
思路: 知等式求递推矩阵,看到递推关系式中的i4,第一要想办法把它化成(i-1)4,所以可得
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long LL;
const LL mod=2147493647;
struct Squ{
LL s[7][7];
};
Squ squ={{1,2,1,4,6,4,1,1,0,0,0,0,0,0,0,0,1,4,6,4,1,0,0,0,1,3,3,1,0,0,0,0,1,2,1,0,0,0,0,0,1,1,0,0,0,0,0,0,1}}; //注意像这样的结构体初始化,只能在它声明的时候
Squ e;
Squ mul(Squ a,Squ b){ //模板
Squ ans;
for(int i=0;i<7;i++)
for(int j=0;j<7;j++){
ans.s[i][j]=0;
for(int k=0;k<7;k++){
ans.s[i][j]+=a.s[i][k]*b.s[k][j];
ans.s[i][j]%=mod;
}
}
return ans;
}
Squ squickpow(Squ a,LL k){ //模板
Squ ans=e;
while(k){
if(k&1) ans=mul(ans,a);
a=mul(a,a);
k/=2;
}
return ans;
}
int main(){
for(int i=0;i<7;i++)
for(int j=0;j<7;j++)
if(i==j) e.s[i][j]=1;
else e.s[i][j]=0;
LL a,b,n;
int t;
scanf("%d",&t);
while(t--){
scanf("%lld%lld%lld",&n,&a,&b);
a%=mod;
b%=mod;
if(n==1) printf("%lld\n",a);
else if(n==2) printf("%lld\n",b);
else{
Squ aa=squickpow(squ,n-2); //想一想,为什么是n-2
LL f0[7]={b,a,16,8,4,2,1};
LL fn[7];
for(int i=0;i<7;i++){
fn[i]=0;
for(int j=0;j<7;j++){
fn[i]+=aa.s[i][j]*f0[j];
fn[i]%=mod;
}
}
printf("%lld\n",fn[0]%mod);
}
}
return 0;
}
找思路,套模板
B-How many ways
题意: 知道n个点,输入m个有向边,给定s,e,k(0 < n <= 20, m <= 100,k < 20),求从s到e且经过k条边的路线 有多少种方案
思路: 就是离散数学中学的邻接矩阵, 设给的初始有向边的矩阵为 C,则C的n次方中,s,e对应矩阵的点c’[s][e]表示从s到e长度为k的路线个数。注意一条边可以走多次。
所以整理一下,只要知道初始矩阵的k次幂后,对应c’[s][e]的大小就是所求的解了~
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
using namespace std;
struct Squ{
int s[25][25];
};
Squ e,squ;
int n,m;
const int mod=1000;
Squ mul(Squ a,Squ b){ //模板,矩阵相乘
Squ squ;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++){
squ.s[i][j]=0;
for(int k=0;k<n;k++)
squ.s[i][j]+=a.s[i][k]*b.s[k][j];
squ.s[i][j]%=mod;
}
return squ;
}
Squ squickpow(Squ squ,int k){ //模板,矩阵快速幂 精髓
Squ ans=e;
while(k){
if(k&1)
ans=mul(ans,squ);
k/=2; squ=mul(squ,squ);
}
return ans;
}
int main(){
for(int i=0;i<25;i++)
for(int j=0;j<25;j++)
if(i==j) e.s[i][j]=1;
else e.s[i][j]=0; //初始化单位矩阵
while(~scanf("%d%d",&n,&m)&&n){
memset(squ.s,0,sizeof(squ.s));
for(int i=0;i<m;i++){
int a,b;
scanf("%d%d",&a,&b);
squ.s[a][b]=1;
}
int t;
scanf("%d",&t);
while(t--){
int s,e,k;
scanf("%d%d%d",&s,&e,&k); //所以正式解题就这三步啊orz
Squ a=squickpow(squ,k); //
int res=a.s[s][e]%mod; //
printf("%d\n",res);
}
}
return 0;
}
Matrix Power Series
题意: 给一个n*n,(n ≤ 30) 的矩阵A,和次数 k (k ≤ 109) 模数m (m < 104),最后求如图的矩阵S,矩阵中每个元素取余m
思路:
思路一:二分
思路二:矩阵套矩阵
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long LL;
struct Squ{
LL s[35][35];
};
struct mSqu{
Squ s[2][2];
}msqu;
Squ squ,e,t;
LL n,mod,k;
Squ mul(Squ a,Squ b){ //矩阵乘法
Squ ans;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++){
ans.s[i][j]=0;
for(int k=0;k<n;k++){
ans.s[i][j]+=a.s[i][k]*b.s[k][j];
ans.s[i][j]%=mod;
}
}
return ans;
}
mSqu mmul(mSqu a,mSqu b){ //元素为矩阵的矩阵乘法,类似矩阵
mSqu ans;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++){
ans.s[i][j]=t;
for(int k=0;k<2;k++){
Squ temp=mul(a.s[i][k], b.s[k][j]);
for(int ii=0;ii<n;ii++){
for(int jj=0;jj<n;jj++){
ans.s[i][j].s[ii][jj]+=temp.s[ii][jj];
ans.s[i][j].s[ii][jj]%=mod;
}
}
}
}
return ans;
}
mSqu msquick(mSqu a,int k){ //元素为矩阵的矩阵快速乘法
mSqu ans;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
if(i==j) ans.s[i][j]=e;
else ans.s[i][j]=t;
while(k){
if(k&1)
ans=mmul(ans,a);
a=mmul(a,a);
k/=2;
}
return ans;
}
int main(){ //只有一组数据
scanf("%lld%lld%lld",&n,&k,&mod);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++){
scanf("%lld",&squ.s[i][j]);
t.s[i][j]=0;
if(i==j) e.s[i][j]=1;
else e.s[i][j]=0;
}
msqu.s[0][0]=squ;
msqu.s[0][1]=msqu.s[1][1]=e;
msqu.s[1][0]=t;
mSqu ans=msquick(msqu,k);
Squ res=t;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
res.s[i][j]=ans.s[0][0].s[i][j]+ans.s[0][1].s[i][j];
if(i==j) res.s[i][j]--; //注意这里-1,因为这种算法多加了一个单位矩阵,需要减去
res.s[i][j]%=mod;
if(j) printf(" ");
printf("%lld",res.s[i][j]);
}
printf("\n");
}
return 0;
}
用矩阵快速幂打辅助的数学分析题
So Easy!
题意: 给出a,m,b,n, (0< a, m < 2^15, (a-1)^2< b < a^2, 0 < b, n < 2^31),求
思路:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long LL;
struct Squ{
LL s[2][2];
};
Squ e={{1,0,0,1}};
Squ squ;
LL a,b,n,mod;
Squ mul(Squ a,Squ b){
Squ ans;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++){
ans.s[i][j]=0;
for(int k=0;k<2;k++){
ans.s[i][j]+=a.s[i][k]*b.s[k][j];
ans.s[i][j]%=mod;
}
}
return ans;
}
Squ squickpow(Squ squ,int k){
Squ ans=e;
while(k){
if(k&1) ans=mul(ans,squ);
k/=2;
squ=mul(squ,squ);
}
return ans;
}
int main(){
while(~scanf("%lld%lld%lld%lld",&a,&b,&n,&mod)){
a%=mod,b%=mod;
LL ans=0;
if(n==1) ans=2*a%mod;
else if(n==2) ans=(2*a*a+2*b)%mod;
else{
squ.s[0][0]=(2*a)%mod;
squ.s[0][1]=(b-a*a+mod*mod)%mod; //注意这里的取模 第一次交WA是因为只加了一个mod,只有加mod*mod才可以
squ.s[1][0]=1;
squ.s[1][1]=0;
Squ res=squickpow(squ,n-2);
LL f1=2*a%mod;
LL f2=(2*a*a+2*b)%mod;
ans=res.s[0][0]%mod*f2%mod+res.s[0][1]*f1%mod;
ans%=mod;
}
printf("%lld\n",ans%mod);
}
return 0;
}