矩陣乘法題目匯總


 

矩陣,學過線性代數的都知道,矩陣滿足結合律,但不滿足交換律

關於矩陣,有很多經典的應用,可以看下大牛的博客http://www.matrix67.com/blog/archives/276

下面的題目中,最常見的一種應用就是利用矩陣求遞推式,可以通過構造矩陣求冪

在這方面,最常見的就是在斐波那契數列方面,可以看下這個博客,超牛的http://www.cnblogs.com/Knuth/archive/2009/09/04/1559951.html

很容易構造出關於斐波那契的矩陣,累乘求冪,就可以求出斐波那契的對應的項

直接開始題目吧

hdu1575 Tr A

題目,直接矩陣求冪,再求該矩陣的跡,注意,利用矩陣乘法滿足結合律,我看可以利用二進制優化求冪次數。比如:A^7=A^4 * A^2 * A^1

具體的結合代碼很容易理解的

View Code
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;

const int MOD = 9973;
const int N = 11;
int ret[N][N],init[N][N],temp[N][N];
int n;
void init_()
{
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
ret[i][j]=(i==j);
}
void matmul(int a[][N],int b[][N])
{
memset(temp,0,sizeof(temp));
for(int i=0;i<n;i++)
for(int k=0;k<n;k++)
if(a[i][k])
for(int j=0;j<n;j++)
if(b[k][j])
temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%MOD;
memcpy(a,temp,sizeof(temp));
}
void q_mod(int k)
{
init_();
for(;k;k>>=1)
{
if(k&1)
matmul(ret,init);
matmul(init,init);
}
}
int main()
{
int T,k;
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&n,&k);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
scanf("%d",&init[i][j]);
q_mod(k);
int ans=0;
for(int i=0;i<n;i++)
ans=(ans+ret[i][i])%MOD;
printf("%d\n",ans);
}
return 0;
}

hdu1005 Number Sequence

題目:很明顯的遞推式求值,與斐波那契數列很類似,只需要重新構造一下矩陣A=

A B

1 0

向量a=(1,1),則當n大於2 時,f(n)=A^(n-2) * a

View Code
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;

const int MOD = 7;
const int N = 2;
int ret[N][N],init[N][N],temp[N][N];
int n;
void init_()
{
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
ret[i][j]=(i==j);
}
void matmul(int a[][N],int b[][N])
{
memset(temp,0,sizeof(temp));
for(int i=0;i<n;i++)
for(int k=0;k<n;k++)
if(a[i][k])
for(int j=0;j<n;j++)
if(b[k][j])
temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%MOD;
memcpy(a,temp,sizeof(temp));
}
void q_mod(int k)
{
init_();
for(;k;k>>=1)
{
if(k&1)
matmul(ret,init);
matmul(init,init);
}
}
int main()
{
int a,b,k;
n=2;
while(scanf("%d %d %d",&a,&b,&k)==3 && (a||b||k))
{
init[0][0]=a,init[0][1]=b;
init[1][0]=1,init[1][1]=0;
if(k<3)
{
printf("1\n");
continue;
}
k-=2;
q_mod(k);
int ans=(ret[0][0]+ret[0][1])%MOD;
printf("%d\n",ans);
}
return 0;
}

hdu1757 A Simple Math Problem

構造如下矩陣即可解決問題咯,很清晰

View Code
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 10;
int n=10,mod;
int ret[N][N],init[N][N],temp[N][N];
void init_()
{
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
ret[i][j]=(i==j);
}
void matmul(int a[][N],int b[][N])
{
memset(temp,0,sizeof(temp));
for(int i=0;i<n;i++)
for(int k=0;k<n;k++)
if(a[i][k])
for(int j=0;j<n;j++)
if(b[k][j])
temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%mod;
memcpy(a,temp,sizeof(temp));
}

void q_mod(int k)
{
init_();
for(;k;k>>=1)
{
if(k&1)
{
matmul(ret,init);
}
matmul(init,init);

}
}
int main()
{
int k;
while(scanf("%d %d",&k,&mod)==2)
{
for(int i=0;i<n;i++)
scanf("%d",&init[0][i]);
if(k<10)
{
printf("%d\n",k);
continue;
}
for(int i=1;i<n;i++)
for(int j=0;j<n;j++)
if(i==j+1)
init[i][j]=1;
else init[i][j]=0;
k-=9;

q_mod(k);
int ans=0;
for(int i=0;i<n;i++)
ans=(ans+ret[0][i]*(n-i-1))%mod;
printf("%d\n",ans);
}
return 0;
}

hdu 2855 Fibonacci Check-up

只要知道這個公式,就可以輕松解決了

View Code
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;

const int N = 2;
int init[N][N],ret[N][N],temp[N][N];
int mod;
void init_(int n)
{
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
ret[i][j]=(i==j);
}
void matmul(int a[][N],int b[][N],int n)
{
memset(temp,0,sizeof(temp));
for(int i=0;i<n;i++)
for(int k=0;k<n;k++)
if(a[i][k])
for(int j=0;j<n;j++)
if(b[k][j])
temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%mod;
memcpy(a,temp,sizeof(temp));
}
void q_mod(int k,int n)
{
init_(n);
for(;k;k>>=1)
{
if(k&1)
matmul(ret,init,n);
matmul(init,init,n);
}
}
int main()
{
int T,n,m;
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&n,&mod);
if(n==0)
{
printf("0\n");
continue;
}
init[0][0]=init[0][1]=1;
init[1][0]=1;
init[1][1]=0;
q_mod(n*2-1,2);
int ans=ret[0][0]%mod;
printf("%d\n",ans);
}
return 0;
}

hdu1588 Gauss Fibonacci

分析:

首先,我們將問題整理一下,就是對等差數列 ai=k*i+b,求所有的f(ai)之和除以M的余數

當0<=i<N

大家有沒有想到,因為ai是等差數列,倘若f(ai)也是個等什么序列,那說不定就有公式求了

f(ai)顯然不是等差數列,直接看上去也不是等比數列

但是如果把f(ai)換成我們剛才所說的Fibonacci矩陣呢?

是的,可是我們對矩陣是直接求冪即可,由於矩陣加法的性質,我們要求A^ai的右上角元素之和,只要求A^ai之和的右上角元素

就矩陣這個東西來說,完全可以看作一個等比數列, 首項是:A^b 公比是:A^k 項數是:N

呵呵,我們可以把問題進一步簡化

因為矩陣的加法對乘法也符合分配律,我們提出一個A^b來,形成這樣的式子: A^b*( I + A^k + (A^k)^2 + .... + (A^k)^(N-1) )

A^b 和 A^k 顯然都可以用我們之前說過的方法計算出來,這剩下一部分累加怎么解決呢

簡單起見,設A^k=B 要求 G(N)=I + ... + B^(N-1),設i=N/2 若N為偶數,G(N)=G(i)+G(i)*B^i 若N為奇數,G(N)=I+ G(i)*B + G(i) * (B^(i+1))

呵呵,這個方法就是比賽當時ACRush用的 而農夫用的則是更精妙的方法,大家可想知道

我們來設置這樣一個矩陣 B I O I 其中O是零矩陣,I是單位矩陣

將它乘方,得到 B^2 I+B O   I 乘三方,得到 B^3 I+B+B^2 O   I 乘四方,得到 B^4 I+B+B^2+B^3 O   I

既然已經轉換成矩陣的冪了,繼續用我們的二分或者二進制法,直接求出冪就可以了 該部分解釋來自http://www.cnblogs.com/Knuth/archive/2009/09/04/1559951.html

第二種方法
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;

const int N =4;
__int64 init[N][N],temp[N][N],ret[N][N];
__int64 bb[N][N],B[N][N];
int mod;
void init_(int n)
{
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
init[i][j]=1;
init[0][0]=0;
for(int i=0;i<N;i++)
for(int j=0;j<N;j++)
ret[i][j]=(i==j);
}
void matmul(__int64 a[][N],__int64 b[][N],int n)
{
memset(temp,0,sizeof(temp));
for(int i=0;i<n;i++)
for(int k=0;k<n;k++)
if(a[i][k])
for(int j=0;j<n;j++)
if(b[k][j])
temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%mod;
memcpy(a,temp,sizeof(temp));
}
void q_mod(int n,int k)
{
for(;k;k>>=1)
{
if(k&1)
matmul(ret,init,n);
matmul(init,init,n);
}
}
void make(int n)
{
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
ret[i][j]=(i==j);
memset(init,0,sizeof(init));
for(int i=0;i<n/2;i++)
for(int j=0;j<n/2;j++)
init[i][j]=B[i][j];
for(int i=0;i<n/2;i++)
for(int j=n/2;j<n;j++)
init[i][j]=ret[i][j-n/2];
for(int i=n/2;i<n;i++)
for(int j=n/2;j<n;j++)
init[i][j]=ret[i-n/2][j-n/2];
}

int main()
{
int n,k,b;
while(scanf("%d %d %d %d",&k,&b,&n,&mod)==4)
{
init_(2);
q_mod(2,b);
memcpy(bb,ret,sizeof(ret));
init_(2);
q_mod(2,k);
memcpy(B,ret,sizeof(ret));
make(4);
q_mod(4,n);
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
ret[i][j]=ret[i][j+2];
matmul(bb,ret,2);
printf("%I64d\n",bb[0][1]%mod);
}
return 0;
}

hdu2157  How many ways??

很有意思的題目,利用圖的鄰接矩陣和矩陣運算的性質

很裸的題目

View Code
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;

const int N = 21;
const int MOD = 1000;
int n,m;
int init[N][N],ret[N][N],temp[N][N];
int g[N][N];
void init_()
{
memcpy(init,g,sizeof(g));
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
ret[i][j]=(i==j);
}
void matmul(int a[][N],int b[][N])
{
memset(temp,0,sizeof(temp));
for(int i=0;i<n;i++)
for(int k=0;k<n;k++)
if(a[i][k])
for(int j=0;j<n;j++)
if(b[k][j])
temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%MOD;
memcpy(a,temp,sizeof(temp));
}
void q_mod(int k)
{
init_();
for(;k;k>>=1)
{
if(k&1)
matmul(ret,init);
matmul(init,init);
}
}


int main()
{
while(scanf("%d %d",&n,&m)==2 && (n||m))
{
int a,b,t;
memset(g,0,sizeof(g));
for(int i=0;i<m;i++)
{
scanf("%d %d",&a,&b);
g[a][b]=1;
}
scanf("%d",&t);
while(t--)
{
int k;
scanf("%d %d %d",&a,&b,&k);
q_mod(k);
printf("%d\n",ret[a][b]);
}
}
return 0;
}

hdu3306 Another kind of Fibonacci

一開始面對這個平方和束手無策呀,后來將利用遞推式將平方展開之后,發現還是一個遞推式,而且我們S(n)=S(n-1)+A(n)^2

所以,就可以構造出一個矩陣出來了

View Code
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
const int N = 4;
const __int64 MOD = 10007;
__int64 init[N][N],temp[N][N],ret[N][N];
__int64 x,y;
void init_()
{
memset(init,0,sizeof(init));
init[0][0]=init[0][1]=init[2][1]=1;
init[1][1]=((x%MOD)*x)%MOD;
init[1][2]=((y%MOD)*y)%MOD;
init[1][3]=((2*x)%MOD*y)%MOD;
init[3][1]=x%MOD;
init[3][3]=y%MOD;
for(int i=0;i<N;i++)
for(int j=0;j<N;j++)
ret[i][j]=(i==j);
}
void matmul(__int64 a[][N],__int64 b[][N])
{
memset(temp,0,sizeof(temp));
for(int i=0;i<N;i++)
for(int k=0;k<N;k++)
if(a[i][k])
for(int j=0;j<N;j++)
if(b[k][j])
temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%MOD;
memcpy(a,temp,sizeof(temp));
}
void q_mod(int k)
{
init_();
for(;k;k>>=1)
{
if(k&1)
matmul(ret,init);
matmul(init,init);
}
}
int main()
{
__int64 n;
while(scanf("%I64d %I64d %I64d",&n,&x,&y)==3)
{
q_mod(n);
__int64 ans=0;
for(int i=0;i<N;i++)
ans=(ans+ret[0][i])%MOD;
printf("%I64d\n",ans);
}
return 0;
}

hdu2604 Queuing

關鍵還是求出遞推式子,糾結了好久,利用DFA,有限自動機,找出狀態之間的轉移

這里解釋的很詳細http://blog.chinaunix.net/uid-24323834-id-261400.html

View Code
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 4;
int init[N][N],temp[N][N],ret[N][N];
int mod;
void init_()
{
memset(init,0,sizeof(init));
init[0][0]=init[0][2]=init[0][3]=1;
for(int i=1;i<N;i++)
for(int j=0;j<N;j++)
if(i==j+1)
init[i][j]=1;
for(int i=0;i<N;i++)
for(int j=0;j<N;j++)
ret[i][j]=(i==j);
}
void matmul(int a[][N],int b[][N])
{
memset(temp,0,sizeof(temp));
for(int i=0;i<N;i++)
for(int k=0;k<N;k++)
if(a[i][k])
for(int j=0;j<N;j++)
if(b[k][j])
temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%mod;
memcpy(a,temp,sizeof(temp));
}
void q_mod(int k)
{
init_();
for(;k;k>>=1)
{
if(k&1)
matmul(ret,init);
matmul(init,init);
}
}
int main()
{
int n;
int f[5]={0,2,4,6,9};
while(scanf("%d %d",&n,&mod)==2)
{
if(n<=4)
{
printf("%d\n",f[n]%mod);
continue;
}
q_mod(n-4);
int ans=0;
for(int i=0;i<4;i++)
ans=(ans+ret[0][i]*f[4-i])%mod;
printf("%d\n",ans);
}
return 0;
}

 

hdu3519 Lucky Coins Sequence

題意:同樣,還是遞推式的問題

給你n個硬幣,問你存在連續相同(正面or反面)長度>2的排列數。

 n很大,顯然dp還要加上優化,n<=(10^9),很顯然的是用矩陣。

 dp[i][j]表示前i個硬幣最后j個是連續的。

 那么dp[i][j]只有兩種轉移方法:dp[i+1][j+1],dp[i+1][1];

因為長度一旦超過2,我們就可以確定這個排列是符合要求的,后面的任何一位不管是正面還是反面都滿足要求,所以就可以算出dp[i][3]*2^(n-i);

一旦出現了連續三個就可以滿足這個式子。

dp[i][3]=dp[i-1][2];

dp[i][2]=dp[i-1][1];

dp[i][1]=dp[i-1][1]+dp[i-1][2];

dp[1][1]=2;dp[1][2]=0;dp[1][3]=0;

這里我們引入多一列dp[i][4]記錄前面滿足條件的總數;

dp[i][4]=dp[i-1][3]+dp[i-1][4]*2(這里乘2,就是前面滿足條件的排列在第i位可以任意放);

View Code
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
const int MOD = 10007;
const int N =4;
int init[N][N],ret[N][N];
void init_()
{
for(int i=0;i<N;i++)
for(int j=0;j<N;j++)
ret[i][j]=(i==j);
memset(init,0,sizeof(init));
init[0][0]=2;
init[0][1]=init[1][2]=init[2][3]=init[3][3]=init[3][2]=1;
}
void matmul(int a[][N],int b[][N])
{
int temp[N][N]={0};
for(int i=0;i<N;i++)
for(int k=0;k<N;k++)
if(a[i][k])
for(int j=0;j<N;j++)
if(b[k][j])
temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%MOD;
memcpy(a,temp,sizeof(temp));
}
void q_mod(int k)
{
init_();
for(;k;k>>=1)
{
if(k&1)
matmul(ret,init);
matmul(init,init);
}
}
int main()
{
int n;
while(scanf("%d",&n)==1)
{
if(n<=2)
{
printf("%d\n",0);
continue;
}
q_mod(n);
printf("%d\n",(ret[0][3]*2)%MOD);
}
return 0;
}

hdu2294 Pendant

題意:求出長度為1~n的串中包含K種顏色的珠子的方案數

構造完矩陣之后,之后就是跟hdu1588類似的過程,注意矩陣中的值,哪一個才是想要求的

View Code
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
const int N = 61;
const __int64 MOD = 1234567891;
__int64 init[N][N],ret[N][N],temp[N][N];
void init_(int k)
{
memset(init,0,sizeof(init));
for(int i=0;i<k-1;i++)
{
init[i][i]=k-i;
init[i][i+1]=k-i-1;
}
init[k-1][k-1]=1;
for(int i=0;i<k;i++)
init[i][i+k]=1;
for(int i=k;i<2*k;i++)
init[i][i]=1;
for(int i=0;i<2*k;i++)
for(int j=0;j<2*k;j++)
ret[i][j]=(i==j);
}
void matmul(__int64 a[][N],__int64 b[][N],int n)
{
memset(temp,0,sizeof(temp));
for(int i=0;i<n;i++)
for(int k=0;k<n;k++)
if(a[i][k])
for(int j=0;j<n;j++)
if(b[k][j])
temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%MOD;
memcpy(a,temp,sizeof(temp));
}
void q_mod(int n,int k)
{
init_(n);
for(;k;k>>=1)
{
if(k&1)
matmul(ret,init,2*n);
matmul(init,init,2*n);
}
}
int main()
{
int T,n,k;
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&n,&k);
q_mod(k,n);
printf("%I64d\n",(ret[0][2*k-1]*k)%MOD);
}
return 0;
}

hdu2971 Tower

與3306完全類似,不過要注意最后負數的處理

View Code
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 4;
__int64 mod;
__int64 ret[N][N],init[N][N];
void init_(__int64 a2)
{
memset(init,0,sizeof(init));
init[0][0]=init[0][3]=init[1][3]=init[3][1]=1;
init[2][2]=-1;
init[0][1]=init[1][1]=(((4*a2)%mod)*a2)%mod;
init[0][2]=init[1][2]=((-4)*a2)%mod;
init[2][1]=(2*a2)%mod;
for(int i=0;i<N;i++)
for(int j=0;j<N;j++)
ret[i][j]=(i==j);
}
void matmul(__int64 a[][N],__int64 b[][N])
{
__int64 temp[N][N]={0};
for(int i=0;i<N;i++)
for(int k=0;k<N;k++)
if(a[i][k]!=0)
for(int j=0;j<N;j++)
if(b[k][j]!=0)
temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%mod;
memcpy(a,temp,sizeof(temp));
}
void q_mod(int k,__int64 a2)
{
init_(a2);
for(;k;k>>=1)
{
if(k&1)
matmul(ret,init);
matmul(init,init);
}
}
int main()
{
int T,n;
__int64 v[4],a2;
scanf("%d",&T);
while(T--)
{
scanf("%I64d %d %I64d",&a2,&n,&mod);
if(n==2)
{
printf("%I64d\n",(((a2%mod)*a2)%mod+1)%mod);
continue;
}
v[0]=(((a2%mod)*a2)%mod+1)%mod;
v[1]=((a2%mod)*a2)%mod;
v[2]=a2%mod;
v[3]=1;
q_mod(n-2,a2);
__int64 ans=0;
for(int i=0;i<N;i++)
ans=(ans+ret[0][i]*v[i])%mod;
if(ans<0)
printf("%I64d\n",ans+mod);
else printf("%I64d\n",ans);
}
return 0;
}

hdu2254 奧運

同樣,利用圖的鄰接矩陣的性質,不過要先離散化,矩陣不大,可以直接打表

View Code
#include<iostream>
#include<algorithm>
#include<string>
#include<map>
using namespace std;
const int MOD =2008;
const int N = 30;
int g[N][N];
int ans[10001][N][N];
map<int,int> ms;
void matmul(int a[N][N],int m,int n)
{
int temp[N][N]={0};
for(int i=0;i<n;i++)
for(int k=0;k<n;k++)
if(a[i][k])
for(int j=0;j<n;j++)
if(g[k][j])
temp[i][j]=(temp[i][j]+a[i][k]*g[k][j])%MOD;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
ans[m][i][j]=temp[i][j];
}
int main()
{
int n,m,t1,t2,u,v;
int v1,v2;
while(scanf("%d",&n)==1)
{
ms.clear();
int num=0;
memset(g,0,sizeof(g));
for(int i=0;i<n;i++)
{
scanf("%d %d",&u,&v);
if(ms.find(u)==ms.end())
ms[u]=num++;
if(ms.find(v)==ms.end())
ms[v]=num++;
g[ms[u]][ms[v]]++;
}
memcpy(ans[0],g,sizeof(g));
for(int i=1;i<=10000;i++)
{
matmul(ans[i-1],i,num);
}
scanf("%d",&m);
while(m--)
{
__int64 ans1=0;
scanf("%d %d %d %d",&v1,&v2,&t1,&t2);
if(ms.find(v1)==ms.end()||ms.find(v2)==ms.end())
{
printf("0\n");
continue;
}
if(t1>t2)
swap(t1,t2);
for(int i=t1==0?t1:t1-1;i<t2;i++)
ans1=(ans1+ans[i][ms[v1]][ms[v2]])%MOD;
printf("%I64d\n",ans1);
}

}
return 0;
}

hdu3658  How many words

求遞推式的方式跟之前的很類似,不過首先我們要知道,要使用矩陣來實現狀態的轉移是有條件的,每一個狀態之間的轉移必須情況是一樣的,同時,是在二維的狀態上轉移,如果直接求的話,我們需要記錄的狀態有三個,是否為符合要求的串,也就是串中是否有至少一對相鄰的字母的ASCii相差32,以什么字母結尾,還有當前的長度,,很明顯,這三個狀態都是必不可少的,那該怎么辦,正難則反,“至少一個”,對立面是什么,我們可以考慮求出相鄰字母小於等於32的字符串的方案數和相鄰字母相差小於32的字符串的方案數,那么倆者相減即為所有了,這倆種都可以通過構造矩陣求出

View Code
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
const int N = 52;
const __int64 MOD = 1000000007;
__int64 ret[N][N],init[N][N];
__int64 A[N][N],B[N][N];
void matmul(__int64 a[][N],__int64 b[][N])
{
__int64 temp[N][N]={0};
for(int i=0;i<N;i++)
for(int k=0;k<N;k++)
if(a[i][k])
for(int j=0;j<N;j++)
if(b[k][j])
temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%MOD;
memcpy(a,temp,sizeof(temp));
}
void q_mod(int k)
{
for(int i=0;i<N;i++)
for(int j=0;j<N;j++)
ret[i][j]=(i==j);
for(;k;k>>=1)
{
if(k&1)
matmul(ret,init);
matmul(init,init);
}
}
int main()
{
char letter[N];
memset(A,0,sizeof(A));
memset(B,0,sizeof(B));
for (int i = 'a';i <= 'z';i++)
{
letter[i - 'a'] = i;
letter[i - 'a' + 26] = i - 'a' + 'A';
}
for (int i = 0;i < N;i++)
{
for (int j = 0;j < N;j++)
{
if (abs(letter[i] - letter[j]) <= 32) A[i][j] = 1;
if (abs(letter[i] - letter[j]) < 32) B[i][j] = 1;
}
}
int T,n;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
__int64 ans1=0,ans2=0;
memcpy(init,A,sizeof(A));
q_mod(n-1);
for(int i=0;i<N;i++)
for(int j=0;j<N;j++)
ans1=(ans1+ret[i][j])%MOD;
memcpy(init,B,sizeof(B));
q_mod(n-1);
for(int i=0;i<N;i++)
for(int j=0;j<N;j++)
ans2=(ans2+ret[i][j])%MOD;
printf("%I64d\n",(ans1-ans2+MOD)%MOD);
}
return 0;
}

hdu2276 Kiki & Little Kiki 2

這題目,我想,如果事先知道是用矩陣做的話,應該不難想出如何構造矩陣了,關鍵是在不知道用矩陣做的情況下,該怎么去思考 呢?

其實,這類題目有一個共同點,就是遞推,或者說是狀態的轉移,而這個題目就很好的詮釋了這一點,當前狀態是否改變與前一個狀態有關,而且,前后狀態的轉移方式是一樣的


View Code
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
const int N = 100;
const int MOD = 2;
int init[N][N],ret[N][N],temp[N][N];
void matmul(int a[][N],int b[][N],int n)
{
memset(temp,0,sizeof(temp));
for(int i=0;i<n;i++)
for(int k=0;k<n;k++)
if(a[i][k])
for(int j=0;j<n;j++)
if(b[k][j])
temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%MOD;
memcpy(a,temp,sizeof(temp));
}
void q_mod(int n,int k)
{
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
ret[i][j]=(i==j);
memset(init,0,sizeof(init));
for(int i=0;i<n;i++)
init[i][i]=1;
for(int i=1;i<n;i++)
init[i][i-1]=1;
init[n-1][n-2]=init[0][n-1]=1;
for(;k;k>>=1)
{
if(k&1)
matmul(ret,init,n);
matmul(init,init,n);
}
}
int main()
{
int n,k;
char str[N+1],ans[N+1];
while(scanf("%d",&k)==1)
{
scanf("%s",str);
n=strlen(str);
q_mod(n,k);
for(int i=0;i<n;i++)
{
int t=0;
for(int j=0;j<n;j++)
t+=ret[i][j]*(str[j]-'0');
ans[i]=t%MOD+'0';
}
ans[n]='\0';
puts(ans);
}
return 0;
}

hdu 3096  Life Game

周期L的大小為10^9 這么大的話,首先應該想到的就是矩陣了,其實主要也是因為周期的狀態值之間的變換是有規律,這種規律性可以轉化為遞推式,很明顯就可以用矩陣來做了

構造一個01矩陣:行表示每一個位置的標號,(i,j)表示 i 位置可以由j位置累加

View Code
#include <iostream>
#include<vector>
#include<algorithm>
#include<string>
const int N = 100;
using namespace std;
int buttom;
int n,mod;
int map[N][N],a[N];
__int64 temp[N][N];
__int64 init[N][N],ret[N][N];
vector<int> g[N];
int dirup[6][2] = {-1,-1, -1,0,  0,-1,0,1,  1,0, 1,1};

int dirmid[6][2] = {-1,-1, -1,0,  0,-1,0,1,  1,-1, 1,0};

int dirdown[6][2] = {-1,0, -1,1,  0,-1,0,1,  1,-1, 1,0};

int input(int n)
{
    int num=0;
    for (int i = 1; i <= 2*n - 1; i++ )
    {
        int tt = i > n ? 3*n -i - 1 : n + i -1;
        for (int j = 1 ; j <= tt; j++)
        {
            map[i][j]=num;//給每一個位置標號
            scanf("%d",&a[num++]);
        }
    }
    for(int i=0;i<num;i++)
        g[i].clear();
    return num;
}
inline bool isok(int i , int j)
{
    // 
    if(i < 1 || i > 2*n - 1)
        return false;
    int tt = i > n ? 3*n -i - 1 : n + i -1;
    if(j < 1 || j > tt)
        return false;
    return true;
}
void build(int num)
{
    memset(temp,0,sizeof(temp));
    for (int i = 1; i <= 2*n - 1; i++ )
    {
        int tt = i > n ? 3*n -i - 1 : n + i -1;
        for (int j = 1 ; j <= tt; j++)
        {
            for (int k = 0 ; k < 6 ; k++)
            {
                int i1,j1;
                if( i < n )
                    i1 = dirup[k][0] + i , j1 = dirup[k][1] +j;
                else if(i == n)
                    i1 = dirmid[k][0] + i , j1 = dirmid[k][1] + j;
                else
                    i1 = dirdown[k][0] + i , j1 = dirdown[k][1] + j;
                if(isok(i1,j1))
                {
                    g[map[i1][j1]].push_back(map[i][j]);
                }
            }
        }
    }
    memset(init,0,sizeof(init));
    for(int i=0;i<num;i++)//構造初始矩陣
    {
        init[i][i]=1;//要加上自身的值
        for(int j=0;j<g[i].size();j++)//標號為i的位置可以由g[i][j]得到
        {
            init[i][g[i][j]]=1;
        }
    }        
}
void matmul(__int64 a[][N],__int64 b[][N],int nn)
{
    memset(temp,0,sizeof(temp));
    for(int i=0;i<nn;i++)
        for(int k=0;k<nn;k++)
            if(a[i][k])
                for(int j=0;j<nn;j++)
                    if(b[k][j])
                        temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%mod;
    memcpy(a,temp,sizeof(temp));
}
void q_mod(int nn,int k)
{
    for(int i=0;i<nn;i++)
        for(int j=0;j<nn;j++)
            ret[i][j]=(i==j);
    for(;k;k>>=1)
    {
        if(k&1)
            matmul(ret,init,nn);
        matmul(init,init,nn);
    }
}

int main()
{
    int k,cas=0;
    while(~scanf("%d%d%d",&n,&mod,&k))
    {
        if(!(n||k||mod))
            break;
        int nn=input(n);
        build(nn);
        q_mod(nn,k);
        __int64 ans=0;
        for(int i=0;i<nn;i++)
        {
            __int64 b=0;
            for(int j=0;j<nn;j++)
            {
                b+=ret[i][j]*a[j];
                if(b>=mod)
                    b%=mod;
            }
            ans+=b;//注意這里不能取模
        }
        printf("Case %d: %I64d\n",++cas,ans);
    }
    return 0;
}

 

hdu3483 A Very Simple Problem

題意:計算

不是自己想出來的,,關於二項式系數,一開始沒想到,,,

View Code
/*求sum(x^k*k^x) k=1~N  
x^(k+1)*(k+1)^x=x^k*x*(k+1)^x 然后用二項式定理展開(k+1)^x即可  
例如當x=4時    
| 1x  0  0  0  0  0 | |x^k*k^0| |x^(k+1)*(k+1)^0|  
| 1x 1x  0  0  0  0 | |x^k*k^1| |x^(k+1)*(k+1)^1|  
| 1x 2x 1x  0  0  0 |*|x^k*k^2|=|x^(k+1)*(k+1)^2|  
| 1x 3x 3x 1x  0  0 | |x^k*k^3| |x^(k+1)*(k+1)^3|  
| 1x 4x 6x 4x 1x  0 | |x^k*k^4| |x^(k+1)*(k+1)^4|  
| 1x 4x 6x 4x 1x 1x | | S(k)  | |     S(k+1)    | */ 

#include<iostream>
#include<algorithm>
using namespace std;

const int N = 50+10;

__int64 init[N][N],ret[N][N],temp[N][N];
int mod;


void init_mat(int x)
{
    memset(init,0,sizeof(init));
    for(int i=0;i<x+2;i++)
        for(int j=0;j<=i;j++)
        {
            if(j==0 || j==i)
                init[i][j]=x;
            else {
                if(i!=x+1)
                    init[i][j]=init[i-1][j-1]+init[i-1][j];//利用楊輝三角的性質求二項式系數
                else init[i][j]=init[i-1][j];
            init[i][j]%=mod;
            }
        }
    init[x+1][x+1]=1;
}
void matmul(__int64 a[][N],__int64 b[][N],int n)
{
    memset(temp,0,sizeof(temp));
    for(int i=0;i<n;i++)
        for(int k=0;k<n;k++)
            if(a[i][k])
                for(int j=0;j<n;j++)
                    if(b[k][j])
                    {
                        temp[i][j]=temp[i][j]+a[i][k]*b[k][j];
                        if(temp[i][j]>=mod)
                            temp[i][j]%=mod;
                    }
    memcpy(a,temp,sizeof(temp));
}

void q_mod(int k,int n)
{
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            ret[i][j]=(i==j);
    for(;k;k>>=1)
    {
        if(k&1)
            matmul(ret,init,n);
        matmul(init,init,n);
    }
}

int main()
{
    int n,x;
    while(scanf("%d %d %d",&n,&x,&mod)==3)
    {
        if(n<0 && x<0 && mod<0)
            break;
        init_mat(x);
        q_mod(n-1,x+2);
        __int64 ans=0;
        for(int i=0;i<x+2;i++)
        {
            ans+=ret[x+1][i]*x;
            if(ans>=mod)
                ans%=mod;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

 

hdu3509 Buge's Fibonacci Number Problem

解決了上一個題目,那這個題目也很好解決了

View Code
/*
    題意: F[n] = a*F[n-1]+b*F[n-2]   求 Sn = ∑F[i]^k
    矩陣乘法
    Sn = ∑F[i]^k = F[n]^k + Sn-1 = (aF[n-1]+bF[n-2])^k + Sn-1           二項式定理展開
       =  C(k,0)F[n-1]^kF[n-2]^0 +  + C(k,k)F[n-1]^0F[n-2]^k + Sn-1            *
    構造向量 
    T(n-1) = (F[n-1]^kF[n-2]^0 ,  , F[n-1]^0F[n-2]^k , Sn-1)
    則T(n) = (F[n]^kF[n-1]^0 ,  , F[n]^0F[n-1]^k , Sn)
    現在的問題是如何構造出矩陣A,使得  T(n-1)*A = T(n)
    由式子*,我們能知道A的最后一列(第K+1列)是(C(k,0) ,  , C(k,k),1)T

    現在剩下的問題是維護向量剩下的那些項,即F[n-1]^xF[n-2]^(k-x)
    同樣是用二項式定理展開即可,這里不贅述了
*/

#include<algorithm>
#include<iostream>
#include<math.h>
using namespace std;

const int N = 50+10;

__int64 init[N][N],ret[N][N],temp[N][N],C[N][N];
__int64 mod;
__int64 xx[N];
__int64 power(__int64 a,int k)
{
    __int64 res=1;
    for(;k;k>>=1)
    {
        if(k&1) res=(res*a)%mod;
        a*=a;
        if(a>=mod) a%=mod;
    }
    return res;
}

void show(__int64 a[N][N],int n)
{
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
            cout<<a[i][j]<< ' ';
        cout<<endl;
    }
}
void init_mat(int x,__int64 a,__int64 b,__int64 f1,__int64 f2)
{
    for(int i=0;i<=x;i++)
        C[i][i]=C[i][0]=1;
    for(int i=2;i<=x;i++)
        for(int j=1;j<i;j++)
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
    memset(init,0,sizeof(init));
    for(int i=0;i<x+1;i++)
        for(int j=0;j<=x-i;j++)
            init[i][j]=(((C[x-i][j]*power(a,x-i-j))%mod)*power(b,j))%mod;
    for(int j=0;j<x+1;j++)
        init[x+1][j]=init[0][j];
    init[x+1][x+1]=1;
    for(int i=0;i<x+1;i++)
    {
        xx[i]=power(f2,x-i)*power(f1,i);
        if(xx[i]>=mod)
            xx[i]%=mod;
    }
    xx[x+1]=power(f1,x)+power(f2,x);
    if(xx[x+1]>=mod) xx[x+1]%=mod;
    //show(init,x+2);
}

void matmul(__int64 a[][N],__int64 b[][N],int n)
{
    memset(temp,0,sizeof(temp));
    for(int i=0;i<n;i++)
        for(int k=0;k<n;k++)
            if(a[i][k])
                for(int j=0;j<n;j++)
                    if(b[k][j])
                    {
                        temp[i][j]=temp[i][j]+a[i][k]*b[k][j];
                        if(temp[i][j]>=mod)
                            temp[i][j]%=mod;
                    }
    memcpy(a,temp,sizeof(temp));
}

void q_mod(int k,int n)
{
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            ret[i][j]=(i==j);
    for(;k;k>>=1)
    {
        if(k&1)
            matmul(ret,init,n);
        matmul(init,init,n);
    }
}

int main()
{
    int n,k;
    int T;
    __int64 f1,f2,a,b;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%I64d %I64d %I64d %I64d %d %d %I64d",&f1,&f2,&a,&b,&k,&n,&mod);
        if(n==1)
        {
            printf("%I64d\n",power(f1,k));
            continue;
        }
        else if(n==2)
        {
            __int64 ans=power(f1,k)+power(f2,k);
            if(ans>=mod) ans%=mod;
            printf("%I64d\n",ans);
            continue;
        }
        init_mat(k,a,b,f1,f2);
        q_mod(n-2,k+2);
        //show(ret,k+2);
        __int64 ans=0;
        for(int i=0;i<k+2;i++)
            ans=(ans+ret[k+1][i]*xx[i])%mod;
        printf("%I64d\n",ans);
    }
    return 0;
}

 


以上部分圖片來自《矩陣乘法題目總結》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM