标题:铺瓷砖
为了让蓝桥杯竞赛更顺利的进行,主办方决定给竞赛的机房重新铺放瓷砖。机房可以看成一个n*m的矩形,而这次使用的瓷砖比较特别,有两种形状,如【图1.png】所示。在铺放瓷砖时,可以旋转。
主办方想知道,如果使用这两种瓷砖把机房铺满,有多少种方案。
【输入格式】
输入的第一行包含两个整数,分别表示机房两个方向的长度。
【输出格式】
输出一个整数,表示可行的方案数。这个数可能很大,请输出这个数除以65521的余数。
【样例输入1】
4 4
【样例输出1】
2
【样例说明1】
这两种方案如下【图2.png】所示:
【样例输入2】
2 6
【样例输出2】
4
【数据规模与约定】
对于20%的数据,1<=n, m<=5。
对于50%的数据,1<=n<=100,1<=m<=5。
对于100%的数据,1<=n<=10^15,1<=m<=6。
资源约定:
峰值内存消耗(含虚拟机) < 512M
CPU消耗 < 8000ms
求解使用两种瓷砖铺满一个n*m的区域的方案数。
对于小范围的数据,可以使用搜索求解。也可以使用搜索在自己的电脑上先算出方案数,将这些答案保存在程序中,直接输出答案即可。
对于稍微大一点的数据,可以使用状态压缩的动态规划来求解。有两种瓷砖,状态的转移比较复杂,
对于100%的数据,需要将在状态压缩的动态规划中去除无用状态,然后建立矩阵表示,使用矩阵乘法的方法来实现状态压缩中的递推。
//瓷砖铺放
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
using namespace std;
const int MOD = 65521;
const int MM = 800;
const int RSIZE = 128;
const int MATRIX_SIZE = 256;
class Matrix {
public:
unsigned int s[MATRIX_SIZE][MATRIX_SIZE];
};
long long n;
int m, M;
int tt[MM][RSIZE], fr[MM][RSIZE];
int *cur[MM], *curFr[MM];
int tc[MM], nc[MM];
Matrix tm, dst, tmpm;
bool mark[MM], canReach[MM];
int remap[MM], who[MM];
void search(int cp, int cl, int nl)
{
if (cp > m)
return ;
if (cp == m)
{
*(cur[cl]++) = nl;
*(curFr[nl]++) = cl;
return ;
}
search(cp+1, cl*3+1, nl*3);
search(cp+1, cl*3+2, nl*3+1);
// (1)
search(cp+2, cl*3*3+1, nl*3*3+3+1);
search(cp+3, cl*3*3*3, nl*3*3*3+9+3+1);
search(cp+4, cl*3*3*3*3, nl*3*3*3*3+27+9+3);
// (2)
search(cp+2, cl*3*3, nl*3*3+1);
// (3)
search(cp+2, cl*3*3, nl*3*3+3);
// (4)
if (cp>0 && nl%3==0)
search(cp+1, cl*3, nl*3+3+1);
// (5)
search(cp+2, cl*3*3+1, nl*3*3+3*2+1);
search(cp+3, cl*3*3*3, nl*3*3*3+9*2+3+1);
search(cp+4, cl*3*3*3*3, nl*3*3*3*3+27*2+9+3);
// (6)
search(cp+3, cl*3*3*3, nl*3*3*3+3);
// (7)
if (cp>0 && nl%3==0)
search(cp+1, cl*3, nl*3+3+2);
// (8)
if (cp>0 && nl%3==0)
{
search(cp+2, cl*3*3+1, nl*3*3+9+3+1);
search(cp+3, cl*3*3*3, nl*3*3*3+27+9+3+1);
search(cp+4, cl*3*3*3*3, nl*3*3*3*3+81+27*2+9+3);
}
}
void goto0(int x)
{
if (canReach[x])
return ;
canReach[x] = true;
for (int *pp = fr[x]; pp < curFr[x]; ++pp)
goto0(*pp);
}
int ccnt = 0;
void go(int x)
{
if (!canReach[x])
return ;
if (mark[x])
return ;
who[ccnt] = x;
remap[x] = ccnt++;
mark[x] = true;
for (int *pp = tt[x]; pp < cur[x]; ++pp)
go(*pp);
}
void pr3(int x, int w)
{
if (w > 1)
pr3(x/3, w-1);
cout << x%3;
}
void prepare()
{
memset(tt, 0, sizeof(tt));
for (int i = 0; i < M; ++i)
{
cur[i] = tt[i];
curFr[i] = fr[i];
}
search(0, 0, 0);
int maxR = 0;
for (int i = 0; i < M; ++i)
if (cur[i]-tt[i] > maxR)
maxR = cur[i] - tt[i];
maxR = 0;
for (int i = 0; i < M; ++i)
if (curFr[i]-fr[i] > maxR)
maxR = curFr[i] - fr[i];
memset(canReach, 0, sizeof(canReach));
goto0(0);
memset(mark, 0, sizeof(mark));
go(0);
memset(&tm, 0, sizeof(tm));
for (int i = 0; i < M; ++i)
if (mark[i])
for (int *pp = tt[i]; pp < cur[i]; ++pp)
if (mark[*pp])
tm.s[remap[i]][remap[*pp]]++;
}
void matrixMul(Matrix &dst, const Matrix &a, const Matrix &b)
{
for (int i = 0; i < ccnt; ++i)
for (int j = 0; j < ccnt; ++j)
{
long long r = 0;
const unsigned int *ai = a.s[i];
for (int k = 0; k < ccnt; ++k)
r += ai[k] * b.s[k][j];
r %= MOD;
dst.s[i][j] = r;
}
}
long long getAns()
{
long long cur = 1;
memset(&dst, 0, sizeof(dst));
for (int i = 0; i < ccnt; ++i)
dst.s[i][i] = 1;
while (n)
{
if (n&cur)
{
matrixMul(tmpm, dst, tm);
dst = tmpm;
n -= cur;
}
matrixMul(tmpm, tm, tm);
tm = tmpm;
cur *= 2;
}
return dst.s[0][0];
}
int main()
{
cin >> n >> m;
M = 1;
for (int i = 0; i < m; ++i)
M *= 3;
prepare();
cout << getAns() << endl;
return 0;
}