矩阵乘法略解

矩阵乘法

对于一个 \(n\times m\) 的矩阵 \(A\) 和一个 \(m\times k\) 的矩阵 \(B\),有且仅有一个 \(n\times k\) 的矩阵 \(C\) 使$$A\times B=C$$且对于 \(\forall i\in [1,n]\ \&\ \forall j\in [1,k]\),有 $$C_{i,j}=\sum^{m}{k=1}{A\times B_{kj}}$$这就是矩阵乘法。比如:

\[\left[\begin{array}{ccc}1&2&3\\3&2&1\\\end{array}\right]\times\left[\begin{array}{ccc}1&1\\2&2\\3&3\\\end{array}\right]=\left[\begin{array}{ccc}14&14\\10&10\\\end{array}\right] \]

题目描述 \(\text{loj10220}\)

大家都知道 Fibonacci 数列吧,\(f_n=f_{n-1}+f_{n-2}\)

现在问题很简单,输入 \(n\)\(m\),求 \(f_n\mod m\)

输入格式

输入 \(n,m\)

输出格式

输出 \(f_n\mod m\)

样例输入

5 1000

样例输出

5

数据范围与提示

对于 \(100\%\) 的数据,\(1\leq n\leq2\times10^9,1\leq m\leq 10^9+10\)

\(\text{Solution 10220}\)

考虑使用矩阵快速幂优化时间复杂度。
若矩阵 \(A\) 使$$\left[
\begin{array}{ccc}
f_{i-2}&f_{i-1}
\end{array}
\right] \times A=\left[
\begin{array}{ccc}
f_{i-1} & f_i
\end{array}
\right]$$则

\[\left[ \begin{array}{ccc} f_n & f_{n-1} \end{array} \right]=\left[ \begin{array}{ccc} f_1&f_2 \end{array} \right]\times A^{n-1}$$而$A^{n-1}$可以使用快速幂求出。 下面推导 $A$。 由矩阵乘法知 $A$ 是一个 $2\times 2$ 的矩阵,且 $$f_{i-1}=A_{11}\times f_{i-2}+A_{21}\times f_{i-1}$$$\therefore A_{11}=0,A_{21}=1,$ 同理得 $A_{12}=1,A_{22}=1,$ $\therefore A=\left[ \begin{array}{ccc} 0 & 1\\ 1 & 1 \end{array} \right].$ ```cpp #include<cstdio> #include<cstdlib> #include<cstring> #define reg register typedef long long ll; int n,m; struct node{ ll a[3][3]; int x,y; node(){ x=y=0; memset(a,0,sizeof(a)); } void pt(){ printf("%lld",a[1][2]); } }s,t,ans; node T(node a,node b){ node c;c.x=a.x;c.y=b.y; for(reg int i=1;i<=c.x;++i) for(reg int j=1;j<=c.y;++j) for(reg int k=1;k<=a.y;++k) c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%m; return c; } node QT(node a,int b){ if(b<=1) return a; node c=QT(a,b/2); if(b%2) return T(T(c,c),a); else return T(c,c); } int main(){ scanf("%d%d",&n,&m); s.x=1;s.y=2;s.a[1][1]=s.a[1][2]=1; t.x=2;t.y=2;t.a[1][1]=0;t.a[1][2]=t.a[2][1]=t.a[2][2]=1; ans=T(s,QT(t,n-2)); ans.pt(); } ``` ## 题目描述 [$\text{loj10221}$](https://loj.ac/problem/10221) 大家都知道 Fibonacci 数列吧,$f_n=f_{n-1}+f_{n-2}$。 求$s_n=\sum^{n}_{k=1}{f_k} \mod m$的值。 ## 样例输入 ``` 5 1000 ``` ## 样例输出 ``` 12 ``` ## 数据范围与提示 对于 $100\%$ 的数据,$1\leq n\leq2\times10^9,1\leq m\leq 10^9+10$ 。 ## $\text{Solution 10221}$ 对于Fibonacci数列前 $n$ 项和 $s_n$,显然有 $s_n=s_{n-1}+f_n$。类似的,我们可以构造一矩阵 $B$,使 $\left[ \begin{array}{ccc} f_{i-2}&f_{i-1}&s_{i-1}\\ \end{array} \right] \times B=\left[ \begin{array}{ccc} f_{i-1}&f_i& s_i\\ \end{array} \right]$,就能完成此题。 ```cpp #include<cstdio> #include<cstdlib> #include<cstring> #define reg register typedef long long ll; int n,m; struct node{ ll a[4][4]; int x,y; node(){ x=y=0; memset(a,0,sizeof(a)); } void pt(){ printf("%lld",a[1][3]); } }s,t,ans; node T(node a,node b){ node c;c.x=a.x;c.y=b.y; for(reg int i=1;i<=c.x;++i) for(reg int j=1;j<=c.y;++j) for(reg int k=1;k<=a.y;++k) c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%m; return c; } node QT(node a,int b){ if(b<=1) return a; node c=QT(a,b/2); if(b%2) return T(T(c,c),a); else return T(c,c); } int main(){ scanf("%d%d",&n,&m); s.x=1;s.y=3;s.a[1][1]=s.a[1][2]=1;s.a[1][3]=2; t.x=3;t.y=3;t.a[1][1]=t.a[3][1]=t.a[3][2]=0;t.a[1][2]=t.a[1][3]=t.a[2][1]=t.a[2][2]=t.a[2][3]=t.a[3][3]=1;; ans=T(s,QT(t,n-2)); ans.pt(); } ``` ## 题目描述 [$\text{loj10222}$](https://loj.ac/problem/10222) 大家都知道 Fibonacci 数列吧,$f_n=f_{n-1}+f_{n-2}$。 求 $T_n=\sum^{n}_{k=1}{k·f_k} \mod m$ 的值。 ## 样例输入 ``` 5 5 ``` ## 样例输出 ``` 4 ``` ## 数据范围与提示 对于 $100\%$ 的数据,$1\leq n,m\leq 2^{31}-1$ 。 ## $\text{Solution 10222}$ 考虑构造一 $1\times 4$ 矩阵 $$A_i=\left[ \begin{array}{ccc} f_{i-2}&f_{i-1}&T_{i-1}&i-1 \end{array} \right]\]

使

\[A_i\times B=A_{i+1} \]

其中 \(B\) 是一个 \(4\times 4\) 的矩阵。同上文,我们有

\[f_{i-1}=B_{11}·f_{i-2}+B_{21}·f_{i-1}+B_{31}·T_{i-1}+B_{41}·(i-1)$$$$f_i=B_{12}·f_{i-2}+B_{22}·f_{i-1}+B_{32}·T_{i-1}+B_{42}·(i-1)$$$$T_i=\ ··· \]

那么,\(T_i\) 怎么用形如上式的表达式表示呢?
事实上,我们无法用形如上式的表达式表示 \(T_i=T_{i-1}+i·f_i\),因为我们无法用方阵 \(B\) 的若干次幂的形式表示 \(i\)

考虑如何将 \(T_i\) 表示成一次递推式。

\[T_n=\sum^{n}_{k=1}{k·f_k}\tag 1$$$$ns_n=\sum^{n}_{k=1}{n·f_k}\tag 2 \]

\((2)-(1)\)

\[ns_n-T_n=\sum^{n}_{k=1}(n-k)·f_k\tag3 \]

\((3)+s_n\)

\[\begin{aligned} ns_n-T_n+s_n&=\sum^{n}_{k=1}{(n-k)·f_k}+s_n\\ &=\sum^{n}_{k=1}{(n-k)·f_k}+\sum^n_{k=1}{f_k}\\ &=\sum^{n}_{k=1}{(n+1-k)·f_k}\\ &=\sum^{n+1}_{k=1}{(n+1-k)·f_k}\\ &=(n+1)s_{n+1}-T_{n+1} \end{aligned}\]

\(p_k=ks_k-T_k\),于是我们得到 \(p\) 的递推关系式

\[p_k=p_{k-1}+s_{k-1}$$所以我们可以构造矩阵 $$A_i'=\left[ \begin{array}{ccc} p_i&s_i&f_i&f_{i-1} \end{array} \right]$$$$B'=\left[ \begin{array}{ccc} 1&1&0&0\\ 0&1&1&0\\ 0&0&1&1\\ 0&0&1&0 \end{array} \right]\]

使

\[A'_i\times B'=A'_{i+1} \]

#include<cstdio>
#include<cstdlib>
#include<cstring>

#define reg register

typedef long long ll;

int n,m;

struct node{
	ll a[5][5];
	int x,y;
	node(){
		x=y=0;
		memset(a,0,sizeof(a));
	}
	void pt(){
		printf("%lld",(a[1][2]*n%m-a[1][1]+m)%m);
	}
}s,t,ans;
node T(node a,node b){
	node c;c.x=a.x;c.y=b.y;
	for(reg int i=1;i<=c.x;++i)
		for(reg int j=1;j<=c.y;++j)
			for(reg int k=1;k<=a.y;++k)
				c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%m;
	return c;
}
node QT(node a,int b){
	if(b<=1) return a;
	node c=QT(a,b/2);
	if(b%2)
		return T(T(c,c),a);
	else
		return T(c,c);
}

int main(){
	scanf("%d%d",&n,&m);
	s.x=1;s.y=4;s.a[1][2]=s.a[1][3]=s.a[1][4]=1;
	t.x=4;t.y=4;t.a[1][1]=t.a[2][1]=t.a[2][2]=t.a[3][2]=t.a[3][3]=t.a[4][3]=t.a[3][4]=1;
	ans=T(s,QT(t,n-1));
	ans.pt();
}

题目描述 \(\text{loj10225}\)

原题来自:SCOI 2009

Windy 在有向图中迷路了。 该有向图有 \(n\) 个节点,Windy 从节点 \(0\) 出发,他必须恰好在 \(T\) 时刻到达节点 。

现在给出该有向图,你能告诉 Windy 总共有多少种不同的路径吗?

注意:Windy 不能在某个节点逗留,且通过某有向边的时间严格为给定的时间。

输入格式

第一行包含两个整数 \(n\)\(T\)
接下来有 \(n\) 行,每行一个长度为 \(n\) 的字符串。第 \(i\) 行第 \(j\) 列为 \(0\) 表示从节点 \(i\) 到节点 \(j\) 没有边,为 \(1\)\(9\) 表示从节点 \(i\) 到节点 \(j\) 需要耗费的时间。

输出格式

包含一个整数,可能的路径数,这个数可能很大,只需输出这个数除以 \(2009\) 的余数。

样例输入 1

2 2
11
00

样例输出 1

1

样例输入 2

5 30
12045
07105
47805
12024
12345

样例输出 2

852

数据范围与提示

对于 \(100\%\) 的数据,满足 \(2\leq n\leq 10,1\leq T\leq 10^9\)

\(\text{Solution 10225}\)

\(n\) 的规模和边权都很小,考虑拆点,则时间复杂度为 \(O(n^T)\)
如何把 \(T\) 优化成 \(\log T\) 呢?观察输入格式,我们很自然地想到矩阵,尝试使用矩阵快速幂优化。
设从 \(i\)\(j\) 的边权为 \(u_{ij}\)。考虑当边权为 \(0\)\(1\) 时,从 \(i\)\(j\) 路径长为 \(T\) 的路径数

\[s_{ijT}=\sum^{n}_{k=1}{(\sum u=T)} \]

如果构造矩阵$$U={u_{ij}|i,j\in [1,n]}$$$$S=U^T$$
则$$S_{ij}=s_{ijT}\ (\forall i,j)$$

#include<cstdio>
#include<cstdlib>
#include<cstring>

#define reg register

typedef long long ll;

int n,t;
const int m=2009;
char str[20][20];

struct node{
	ll a[110][110];
	int x,y;
	node(){
		x=y=0;
		memset(a,0,sizeof(a));
	}
}s,ans;
node T(node a,node b){
	node c;c.x=a.x;c.y=b.y;
	for(reg int i=1;i<=c.x;++i)
		for(reg int j=1;j<=c.y;++j)
			for(reg int k=1;k<=a.y;++k)
				c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%m;
	return c;
}
node QT(node a,int b){
	node c;c.x=n;c.y=n;
	for(reg int i=1;i<=n;++i)
		c.a[i][i]=1;
	while(b){
		if(b&1) c=T(c,a);
		a=T(a,a);b>>=1;
	}
	return c;
}

int main(){
	scanf("%d%d",&n,&t);
	for(reg int i=1;i<=n;++i){
		scanf("%s",str[i]+1);
		for(reg int j=1;j<=n;++j)
			str[i][j]-='0';
	}
	for(reg int i=1;i<=n;++i){
		for(reg int j=1;j<=8;++j)
			s.a[(i-1)*9+j][(i-1)*9+j+1]=1;
		for(reg int j=1;j<=n;++j)
			if(str[i][j])
				s.a[(i-1)*9+str[i][j]][(j-1)*9+1]=1;
	}
	n*=9;s.x=s.y=n;
	ans=QT(s,t);
	printf("%d",ans.a[1][n-8]);
}

题目描述 情书

\[\sum_{k=1}^{n}f_k+\sum_{k=1}^{n}{c^k}\quad(\mod m) \]

的值,其中 \(\forall i>2\)

\[f_i=a·f_{i-1}+b·f_{i-2} \]

输入描述

一行 \(7\) 个整数,分别为 \(a,b,c,n,m,f_1,f_2\),相邻两数以一个空格隔开。

输出描述

一行,一个整数,表示答案。

输入样例

\(\ \ \ \ \text{1 1 1 3 5 1 1}\)

输出样例

\(\ \ \ \ 2\)

数据规模与约定

对于 \(100\%\) 的数据,有 \(0\leq a,b,c,n,m,f_i\leq2^{31}\),其中 \(i\in[1,n]\)

\(\text{Solution 5}\)

Pt.1 求 \(s_k\) 的值

仿照例2知,\(a,b\) 是矩阵 \(B\) 的两个元素,请读者自己尝试推导。

Pt.2 求 \(\sum_{k=1}^{n}{c^k}\)

简单等比数列求和。

\[S=\sum_{k=1}^{n}{c^k}\quad\text{...(4)} \]

\[cS=\sum_{k=1}^{n}{c^{k+1}}\quad\text{...(5)} \]

\((5)-(4)\)

\[(c-1)S=c^{k+1}-c \]

\[S=\frac{c^{k+1}-c}{c-1} \]

使用乘法逆元和快速幂即可完成求解。

#include<cstdio>
#include<cstdlib>
#include<cstring>

#define reg register
typedef long long ll;

struct node{
	int x,y;
	ll a[5][5];
	node(){
		memset(a,0,sizeof(a));
		x=y=0;
	}
}s,t;

ll a,b,c;
ll n,m,x,y,son;
ll sum=0;

node T(node a,node b){
	node c;c.x=a.x;c.y=b.y;
	for(reg int i=1;i<=c.x;++i)
		for(reg int j=1;j<=c.y;++j)
			for(reg int k=1;k<=a.y;++k)
				c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%m;
	return c;
}
node QT(node a,int b){
	if(b<=1) return a;
	node c=QT(a,b/2);
	if(b%2)
		return T(T(c,c),a);
	else
		return T(c,c);
}
void exgcd(ll A,ll B,ll&x,ll&y){
	if(!B){
		x=1;y=0;
		return;
	}
	exgcd(B,A%B,x,y);
	ll t=x;x=y;y=t-A/B*y;
}
ll qt(ll a,int b){
	if(b==1) return a;
	ll e=qt(a,b/2)%m;
	if(b%2)
		return (e*e)%m*a%m;
	else
		return (e*e)%m;
}
int main(){
	scanf("%lld%lld%lld%lld%lld%lld%lld",&a,&b,&c,&n,&m,&s.a[1][1],&s.a[1][2]);c%=m;
	s.a[1][3]=s.a[1][1]+s.a[1][2];s.a[1][4]=s.a[1][1];
	s.x=1;s.y=4;t.x=t.y=4;
	t.a[2][1]=t.a[3][3]=t.a[3][4]=t.a[4][4]=1;
	t.a[1][2]=t.a[1][3]=b;t.a[2][2]=t.a[2][3]=a;
	s=T(s,QT(t,n-2));
	exgcd(c-1,m,x,y);son=(qt(c,n+1)-c+m)%m;x=(x%m+m)%m;
	printf("%lld",(((s.a[1][3]*n)%m-s.a[1][4]+m)%m+(x*son)%m+m)%m);
}
posted @ 2019-10-12 16:29  TeacherDai  阅读(229)  评论(0)    收藏  举报