YbtOJ 「数学基础」第1章 矩阵快速幂

例题1.序列的第 k 个数

等差数列可以直接用乘法求,等比数列写一个普通快速幂。
注意数据范围是 \(10^9\),而不是 \(109\)\(k\le 3\) 的情况记得特判。

code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=200907;
int T,a,b,c,k;
int qpow(int n,int k)
{
	int ans=1;
	while(k)
	{
		if(k&1) ans=ans*n%mod;
		n=n*n%mod;k>>=1;
	}
	return ans;
}
signed main()
{
	scanf("%lld",&T);
	while(T--)
	{
		scanf("%lld%lld%lld%lld",&a,&b,&c,&k);
		if(k==1) {cout<<a<<endl;continue;}
		if(k==2) {cout<<b<<endl;continue;}
		if(b-a==c-b)
		{
			int qwq=b-a;
			c=(c+qwq*(k-3)%mod)%mod;
			cout<<c<<endl;
		}
		else
		{
			int qwq=b/a;
			c=c*qpow(qwq,k-3)%mod;
			cout<<c<<endl;
		}
	}
	return 0;
}

例题2.斐波那契数列

转移矩阵为[1,1,1,0] 脑补换行吧,懒得打 LaTeX (

code
#include<bits/stdc++.h>
#define int long long

using namespace std;
const int mod=1e9+7; 
int n;
struct matrix{
	int n,m,f[3][3];
	matrix(){memset(f,0,sizeof(f));}
};
matrix operator *(const matrix &x,const matrix &y)
{
	matrix res;
	res.n=x.n,res.m=y.m;
	for(int i=1;i<=res.n;i++)
		for(int j=1;j<=res.m;j++)
			for(int k=1;k<=x.m;k++)
				res.f[i][j]=(res.f[i][j]+x.f[i][k]*y.f[k][j]%mod)%mod;
	return res;
}
matrix qpow(matrix n,int k)
{
	matrix res;
	res.n=res.m=2;res.f[1][1]=res.f[2][2]=1;
	while(k>0)
	{
		if(k&1) res=res*n;
		n=n*n;k>>=1;
	}
	return res;
}
signed main()
{
	scanf("%lld",&n);
	matrix a,b;
	a.n=1,a.m=2,a.f[1][1]=a.f[1][2]=1;
	b.n=b.m=2;b.f[1][1]=b.f[1][2]=b.f[2][1]=1;
	matrix ans=a*qpow(b,n-2);
	cout<<ans.f[1][1]<<endl;
	return 0;
}

例题3.行为方案

邻接矩阵的 \(k\) 次方代表走 \(k\) 步后的方案数。
对于自爆,将所有点向 \(0\) 连一条边。

code
#include<bits/stdc++.h>
using namespace std;
const int mod=2017,N=105;
int n,m,t;
struct matrix {
	int n,m,f[N][N];
	matrix(){memset(f,0,sizeof(f));}
}mp;
matrix operator * (const matrix &x,const matrix &y)
{
	matrix res;
	res.n=x.n,res.m=y.m;
	for(int i=0;i<=x.n;i++) 
		for(int j=0;j<=y.m;j++)
			for(int k=0;k<=x.m;k++)
				res.f[i][j]=(res.f[i][j]+x.f[i][k]*y.f[k][j]%mod)%mod;
	return res;
}
matrix qpow(matrix a,int k)
{
	matrix res;res.n=res.m=n;
	for(int i=0;i<=n;i++) res.f[i][i]=1;
	while(k)
	{
		if(k&1) res=res*a;
		a=a*a;k>>=1;
	}
	return res;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1,u,v;i<=m;i++)
	{
		scanf("%d%d",&u,&v);
		mp.f[u][v]=mp.f[v][u]=1;
	}
	mp.n=mp.m=n;
	for(int i=0;i<=n;i++) mp.f[i][0]=1,mp.f[i][i]=1;
	scanf("%d",&t);
	matrix ans=qpow(mp,t);
	int sum=0;
	for(int i=0;i<=n;i++) sum=(sum+ans.f[1][i])%mod;
	cout<<sum<<endl;
	return 0;
}

例题4.矩阵求和

因为求的是整个矩阵,考虑构造一个包含 \(A\) 的转移矩阵。则矩阵 \(B\) 为:\(\begin{bmatrix} A & I\\ 0&I \end{bmatrix}\),其中 \(I\)\(n\times n\) 的单位矩阵。
\(B^k\) 的右上角矩阵为 \(\Sigma^{k-1}_{i=0}A^i\)。故答案为 \(B^{k+1}\) 的右上角减 \(I\)
注意这题坑人,std 取模没判负数所以答案里有 \(-1\)。也就是说你样例右下角那个数跑出来是 \(-1\) 才能 AC。

code
#include<bits/stdc++.h>
using namespace std;
const int N=65;
int n,k,mod;
struct matrix {
	int n,m,f[N][N];
	matrix(){memset(f,0,sizeof(f));}
};
matrix operator * (const matrix &x,const matrix &y)
{
	matrix res;
	res.n=x.n,res.m=y.m;
	for(int i=1;i<=x.n;i++) 
		for(int j=1;j<=y.m;j++)
			for(int k=1;k<=x.m;k++)
				res.f[i][j]=(res.f[i][j]+x.f[i][k]*y.f[k][j]%mod)%mod;
	return res;
}
matrix qpow(matrix a,int k)
{
	matrix res;res.n=res.m=2*n;
	for(int i=1;i<=2*n;i++) res.f[i][i]=1;
	while(k)
	{
		if(k&1) res=res*a;
		a=a*a;k>>=1;
	}
	return res;
}
int main()
{
	matrix a;
	scanf("%d%d%d",&n,&k,&mod);//mod*=2;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			scanf("%d",&a.f[i][j]);
		}
	}
	a.n=a.m=2*n;
	for(int i=n+1;i<=2*n;i++)
		a.f[i][i]=1;
	for(int i=1;i<=n;i++)
		a.f[i][i+n]=1;
	matrix ans=qpow(a,k+1);
	for(int i=1;i<=n;i++) ans.f[i][i+n]=(ans.f[i][i+n]-1)%mod;
	for(int i=1;i<=n;i++)
	{
		for(int j=n+1;j<=2*n;j++)
		{
			cout<<ans.f[i][j]<<" ";
		}
		cout<<endl;
	}
	return 0;
}

例题5.最短路径

书上告诉我们可以把 \(f[i][j][k]=\min\{f[i][t][k-1]+f[t][j][1]\}\) 转化为 \(f[i][j]=\min\{f[i][t]+f[t][j]\}\)。这东西乍一看不太正确的样子,但写完代码发现他想表达的其实是 \(f[i][j][k]=\min\{f[i][t][k-l]+f[t][j][l]\}\) emmm...
这东西长得很像矩阵乘法公式,所以矩阵乘法。res 初始值不能赋成单位矩阵,或许可以直接用原矩阵开始乘。(也可能还有别的方法但窝tcl不会/kk

code
#include<bits/stdc++.h>
using namespace std;
const int N=205;
int n,t,s,e;
int cnt,num[1005];
struct matrix{
	int f[N][N];
	matrix(){memset(f,0x3f,sizeof(f));}
}a;
matrix operator *(const matrix &x,const matrix &y)
{
	matrix res;
	for(int k=1;k<=cnt;k++)
		for(int i=1;i<=cnt;i++)
			for(int j=1;j<=cnt;j++)
				res.f[i][j]=min(res.f[i][j],x.f[i][k]+y.f[k][j]);
	return res;
}
matrix qpow(matrix n,int k)
{
	matrix res=n;
	while(k)
	{
		if(k&1) res=res*n;
		n=n*n;k>>=1;
	}
	return res;
}
int main()
{
	scanf("%d%d%d%d",&t,&n,&s,&e); 
	 
	for(int i=1,u,v,w;i<=n;i++)
	{
		scanf("%d%d%d",&w,&u,&v);
		if(!num[u]) num[u]=++cnt;
		if(!num[v]) num[v]=++cnt;
		a.f[num[u]][num[v]]=a.f[num[v]][num[u]]=w;
	}
	a=qpow(a,t-1);
	cout<<a.f[num[s]][num[e]]<<endl;
	return 0;
}

1.数列求解

构造转移矩阵\(\begin{bmatrix} p & 1\\ q & 0 \end{bmatrix}\)

code
#include<bits/stdc++.h>
#define int long long

using namespace std;
int p,q,a1,a2,n,m;
struct matrix{
	int n,m;
	int f[4][4];
	matrix(){memset(f,0,sizeof(f));}
};
matrix operator *(const matrix &x,const matrix &y)
{
	matrix res;
	res.n=x.n,res.m=y.m;
	for(int i=1;i<=x.n;i++)
		for(int j=1;j<=y.m;j++)
			for(int k=1;k<=x.m;k++)
				res.f[i][j]=(res.f[i][j]+x.f[i][k]*y.f[k][j])%m;
	return res;
}
matrix qpow(matrix n,int k)
{
	matrix res;
	res.n=res.m=2;res.f[1][1]=res.f[2][2]=1;
	while(k)
	{
		if(k&1) res=res*n;
		n=n*n;k>>=1;
	}
	return res;
}
signed main()
{
	scanf("%lld%lld%lld%lld%lld%lld",&p,&q,&a1,&a2,&n,&m);
	matrix a,b;
	a.n=1,a.m=2;a.f[1][1]=a2,a.f[1][2]=a1;
	b.n=b.m=2;
	b.f[1][1]=p,b.f[1][2]=1,b.f[2][1]=q,b.f[2][2]=0;
	matrix ans=a*qpow(b,n-2);
	cout<<ans.f[1][1]<<endl;
	return 0;
}

2.填充棋盘

经找规律,答案为 \(6\times (2^n+2^m)-24\)

code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7; 
int n,m;
int qpow(int n,int k)
{
	int ans=1;
	while(k)
	{
		if(k&1) ans=ans*n%mod;
		n=n*n%mod;k>>=1;
	}
	return ans;
}
signed main()
{
	scanf("%lld%lld",&n,&m);
	cout<<(6*(qpow(2,n)+qpow(2,m))%mod-24+mod)%mod<<endl;
	return 0;
}

3.集合统计

为什么这题没题解啊/qd
分别考虑集合中的每一个元素 \(a_i\)。对于我们选择的子集,要保证至少有一个不含 \(a_i\)。则对于这个元素,选择方案数为 \(2^k-1\)。共有 \(n\) 个元素。故最后的答案为 \((2^k-1)^n\)
这里数据范围是 \(10^{63}\) 超出了 long long 的范围,用欧拉定理处理一下就好了。
hbc学长太强辣!!!

code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
int qpow(int n,int k)
{
	int ans=1;
	while(k)
	{
		if(k&1) ans=ans*n%mod;
		n=n*n%mod;k>>=1;
	}
	return ans;
}
unsigned long long n,k;
int read()
{
	int xr=0;char cr;
	cr=getchar();
	while(cr<'0'||cr>'9') cr=getchar();
	while(cr>='0'&&cr<='9') xr=(xr*10%(mod-1)+(cr-'0'))%(mod-1),cr=getchar();
	return xr;
}
signed main()
{
	n=read();k=read();
	//cout<<n<<" "<<k<<endl;
	n%=(mod-1),k%=(mod-1);
	//cout<<n<<" "<<k<<endl;
	cout<<qpow((qpow(2,k)-1+mod)%mod,n)<<endl;
	return 0;
}

4.公式推导

两两配对,组合数常见套路。
然后就可以得到答案为 \((2s+dn)2^{n-1}\)
这题会爆 long long,要写龟速乘。

code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
int n,s,d;
int qpow(int n,int k)
{
	int ans=1;
	while(k)
	{
		if(k&1) ans=ans*n%mod;
		n=n*n%mod;k>>=1;
	}
	return ans;
}
int mul(int n,int k)
{
	int ans=0;
	while(k)
	{
		if(k&1) ans=(ans+n)%mod;
		n=(n+n)%mod;k>>=1;
	}
	return ans;
}
signed main()
{
	scanf("%lld%lld%lld",&n,&s,&d);
	cout<<(2*s+mul(n,d)%mod)%mod*qpow(2,n-1)%mod<<endl;
	return 0;
}

5.斐波拉契

写一下做这题的心路历程吧。
根据题目和题里的式子联想到斐波那契的通项公式,移项得答案为斐波那契数列对应位置的数乘一个 \(\sqrt5\)。提交,后面一半WA了。精度会炸。
用前面小数据打表。发现答案序列也是一个类似斐波那契的东西(?
然后又WA了。然后发现偶数答案要减1。于是过了。

code
#include<bits/stdc++.h>
#define int long long

using namespace std;
int n,mod;
struct matrix{
	int n,m,f[3][3];
	matrix(){memset(f,0,sizeof(f));}
};
matrix mul(matrix x,matrix y)
{
	matrix res;
	res.n=x.n,res.m=y.m;
	for(int i=1;i<=res.n;i++)
		for(int j=1;j<=res.m;j++)
			for(int k=1;k<=x.m;k++)
				res.f[i][j]=(res.f[i][j]+x.f[i][k]*y.f[k][j]%mod)%mod;
	return res;
}
matrix qpow(matrix n,int k)
{
	matrix res;
	res.n=res.m=2;res.f[1][1]=res.f[2][2]=1;
	while(k>0)
	{
		if(k&1) res=mul(res,n);
		n=mul(n,n);k>>=1;
	}
	return res;
}
signed main()
{
	scanf("%lld%lld",&n,&mod);
	matrix a,b;
	a.n=1,a.m=2,a.f[1][1]=3,a.f[1][2]=1;
	b.n=b.m=2;b.f[1][1]=b.f[1][2]=b.f[2][1]=1;
	if(n==0) {cout<<1<<endl;return 0;}
	matrix ans=mul(a,qpow(b,n-2));
	double qwq=sqrt(5.0);
	cout<<ans.f[1][1]-(n%2==0)<<endl;
	//cout<<(int)(ans.f[1][1]*qwq)%mod<<endl;
	return 0;
}
posted @ 2022-08-13 16:27  樱雪喵  阅读(103)  评论(0编辑  收藏  举报