矩阵快速幂好题分享
poj3420 Quad Tiling
这些方案数的题,一般是先找到某些递推式,然后,根据递推式做之后的优化工作。
首先这是个铺地板的经典问题,对于这类方案数的求解有一个比较重要的思想,就是考虑从小往上的最小的可分割矩阵来分开求方案数。我们首先设\(a_i\)表示i4的方格不可分割的最小矩阵的发案数。这个需要自己画图发现,a1=1,a2=4,a3=2,a4=3...之后发现奇数次为2,偶数次为3.之后考虑递推式n,设f表示它所求的值,那么考虑按最小分割的矩阵分类讨论,f[n]=a1f[n-1]+a2f[n-2]+...+an-1f[1]=f[n-1]+4f[n-2]+2(f[n-3]+f[n-5]...)+3(f[n-4]+f[n-6]+...)
对于这类的递推式子,如果我们要求通项的话,一般的做法就是将f[n-1]写出,然后相加减即可。(有点错位相减的意思。)
那么f[n-1]=f[n-2]+4f[n-3]+2(f[n-4]+f[n-6]+...)+3(f[n-5]+f[n-7]+...)
两者做和,得出f[n]=5f[n-2]+6f[n-3]+5(f[n-4]+f[n-5]+...)
则我们继续,f[n-1]=5f[n-3]+6f[n-4]+5(f[n-5]+f[n-6]+...)
两式相减:f[n]-f[n-1]=5f[n-2]+f[n-3]-f[n-4].则f[n]=f[n-1]+5f[n-2]+f[n-3]-f[n-4].
之后直接用矩阵快速幂递推即可。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <string.h>
#define ll long long
using namespace std;
const int N=5;
int n,m;
struct wy
{
ll a[N][N];
wy() {memset(a,0,sizeof(a));}
inline void clear() {memset(a,0,sizeof(a));}
wy friend operator*(wy a,wy b)
{
wy c;
for(int i=1;i<N;++i)
for(int j=1;j<N;++j)
for(int k=1;k<N;++k) c.a[i][j]=((c.a[i][j]+a.a[i][k]*b.a[k][j])%m+m)%m;
return c;
}
wy friend operator^(wy a,ll y)
{
wy c;
for(int i=1;i<N;++i) c.a[i][i]=1;
while(y)
{
if(y&1) c=c*a;
y>>=1;
a=a*a;
}
return c;
}
};
int main()
{
// freopen("1.in","r",stdin);
while(scanf("%d%d",&n,&m))
{
if(n==0&&m==0) break;
wy A,B,C;
A.a[1][1]=1;A.a[1][2]=5;
A.a[1][3]=11;A.a[1][4]=36;
if(n<=4) printf("%lld\n",A.a[1][n]%m);
else
{
B.a[2][1]=1;B.a[3][2]=1;
B.a[4][3]=1;B.a[1][4]=-1;
B.a[2][4]=1;B.a[3][4]=5;
B.a[4][4]=1;
B=B^(n-4);
C=A*B;
printf("%lld\n",C.a[1][4]%m);
}
}
return 0;
}
#2476. 战场的数目
真的是神仙题目...
首先可以发现战场的周长一定是偶数,这个可以考虑当初没合并之前每个小正方形的周长都是6,合并的时候两个重合的面导致两个面被不计,所以周长一定是偶数。既然如此,我们可以考虑设f[i]表示周长为2i的方案数。接下来的就是讨论方案数中最难的一部分,怎么划分方案,可以用上之前的信息。首先我们可以想到左右两边的1似乎有某些性质,考虑若最左边或最右边有一个1,我们把这个1去掉,减少的周长为2,即f[i-1]。那么这个方案数就是2f[i-1]。但这样会有重复,考虑两边都有1的情况,我们考虑将这两边的1都去掉,即f[i-2]。接下来考虑两边都没1的情况,我们可以将最下面一层去掉。这样仍是我们之前的状态。最后f[i]=3*f[i-1]-f[i-2],最后别忘了减去所有矩阵的方案,即n-1.直接用矩阵加速即可。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int P=987654321;
struct wy
{
ll a[3][3];
wy() {memset(a,0,sizeof(a));}
void clear(){memset(a,0,sizeof(a));}
wy friend operator*(wy a,wy b)
{
wy c;
for(int i=1;i<=2;++i)
for(int j=1;j<=2;++j)
for(int k=1;k<=2;++k) c.a[i][j]=((c.a[i][j]+a.a[i][k]*b.a[k][j])%P+P)%P;
return c;
}
wy friend operator^(wy x,ll y)
{
wy ans;
for(int i=1;i<=2;++i) ans.a[i][i]=1;
while(y)
{
if(y&1) ans=ans*x;
y>>=1;
x=x*x;
}
return ans;
}
}A,B,C;
int main()
{
// freopen("1.in","r",stdin);
int x;
while(scanf("%d",&x))
{
if(!x) break;
if(x%2==1||x<8) printf("%d\n",0);
else
{
if(x==8) {printf("%d\n",2);continue;}
else if(x==10) {printf("%d\n",9);continue;}
int d=x/2;
A.a[1][1]=5;A.a[1][2]=13;
B.a[1][1]=0;B.a[1][2]=-1;
B.a[2][1]=1;B.a[2][2]=3;
B=B^(d-5);
C=A*B;
printf("%lld\n",((C.a[1][2]-(d-1))%P+P)%P);
}
}
return 0;
}