15年国赛 铺瓷砖

标题:铺瓷砖

为了让蓝桥杯竞赛更顺利的进行,主办方决定给竞赛的机房重新铺放瓷砖。机房可以看成一个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;
}

 

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值